📄 dscc4.c
字号:
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); struct dscc4_pci_priv *ppriv; int ret = -EAGAIN; if ((dscc4_loopback_check(dpriv) < 0) || !dev->hard_start_xmit) goto err; if ((ret = hdlc_open(dev))) goto err; ppriv = dpriv->pci_priv; /* * 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; scc_patchl(0, PowerUp | Vis, dpriv, dev, CCR0); /* * The following is a bit paranoid... * * NB: the datasheet "...CEC will stay active if the SCC is in * power-down mode or..." and CCR2.RAC = 1 are two different * situations. */ if (scc_readl_star(dpriv, dev) & SccBusy) { printk(KERN_ERR "%s busy. Try later\n", dev->name); ret = -EAGAIN; goto err_out; } else printk(KERN_INFO "%s: available. Good\n", dev->name); scc_writel(EventsMask, dpriv, dev, IMR); /* Posted write is flushed in the wait_ack loop */ scc_writel(TxSccRes | RxSccRes, dpriv, dev, CMDR); if ((ret = dscc4_wait_ack_cec(dpriv, dev, "Cec")) < 0) goto err_disable_scc_events; /* * I would expect XPR near CE completion (before ? after ?). * At worst, this code won't see a late XPR and people * will have to re-issue an ifconfig (this is harmless). * WARNING, a really missing XPR usually means a hardware * reset is needed. Suggestions anyone ? */ if ((ret = dscc4_xpr_ack(dpriv)) < 0) { printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR"); goto err_disable_scc_events; } if (debug > 2) dscc4_tx_print(dev, dpriv, "Open");done: netif_start_queue(dev); init_timer(&dpriv->timer); dpriv->timer.expires = jiffies + 10*HZ; dpriv->timer.data = (unsigned long)dev; dpriv->timer.function = &dscc4_timer; add_timer(&dpriv->timer); netif_carrier_on(dev); return 0;err_disable_scc_events: scc_writel(0xffffffff, dpriv, dev, IMR); scc_patchl(PowerUp | Vis, 0, dpriv, dev, CCR0);err_out: hdlc_close(dev);err: return ret;}#ifdef DSCC4_POLLINGstatic int dscc4_tx_poll(struct dscc4_dev_priv *dpriv, struct net_device *dev){ /* FIXME: it's gonna be easy (TM), for sure */}#endif /* DSCC4_POLLING */static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct dscc4_pci_priv *ppriv = dpriv->pci_priv; struct TxFD *tx_fd; int next; next = dpriv->tx_current%TX_RING_SIZE; dpriv->tx_skbuff[next] = skb; tx_fd = dpriv->tx_fd + next; tx_fd->state = FrameEnd | TO_STATE_TX(skb->len); tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); tx_fd->complete = 0x00000000; tx_fd->jiffies = jiffies; mb();#ifdef DSCC4_POLLING spin_lock(&dpriv->lock); while (dscc4_tx_poll(dpriv, dev)); spin_unlock(&dpriv->lock);#endif dev->trans_start = jiffies; if (debug > 2) dscc4_tx_print(dev, dpriv, "Xmit"); /* To be cleaned(unsigned int)/optimized. Later, ok ? */ if (!((++dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE)) netif_stop_queue(dev); if (dscc4_tx_quiescent(dpriv, dev)) dscc4_do_tx(dpriv, dev); return 0;}static int dscc4_close(struct net_device *dev){ struct dscc4_dev_priv *dpriv = dscc4_priv(dev); del_timer_sync(&dpriv->timer); netif_stop_queue(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(dev); return 0;}static inline int dscc4_check_clock_ability(int port){ int ret = 0;#ifdef CONFIG_DSCC4_PCISYNC if (port >= 2) ret = -1;#endif return ret;}/* * DS1 p.137: "There are a total of 13 different clocking modes..." * ^^ * Design choices: * - by default, assume a clock is provided on pin RxClk/TxClk (clock mode 0a). * Clock mode 3b _should_ work but the testing seems to make this point * dubious (DIY testing requires setting CCR0 at 0x00000033). * This is supposed to provide least surprise "DTE like" behavior. * - if line rate is specified, clocks are assumed to be locally generated. * A quartz must be available (on pin XTAL1). Modes 6b/7b are used. Choosing * between these it automagically done according on the required frequency * scaling. Of course some rounding may take place. * - no high speed mode (40Mb/s). May be trivial to do but I don't have an * appropriate external clocking device for testing. * - no time-slot/clock mode 5: shameless lazyness. * * The clock signals wiring can be (is ?) manufacturer dependant. Good luck. * * BIG FAT WARNING: if the device isn't provided enough clocking signal, it * won't pass the init sequence. For example, straight back-to-back DTE without * external clock will fail when dscc4_open() (<- 'ifconfig hdlcx xxx') is * called. * * Typos lurk in datasheet (missing divier in clock mode 7a figure 51 p.153 * DS0 for example) * * Clock mode related bits of CCR0: * +------------ TOE: output TxClk (0b/2b/3a/3b/6b/7a/7b only) * | +---------- SSEL: sub-mode select 0 -> a, 1 -> b * | | +-------- High Speed: say 0 * | | | +-+-+-- Clock Mode: 0..7 * | | | | | | * -+-+-+-+-+-+-+-+ * x|x|5|4|3|2|1|0| lower bits * * Division factor of BRR: k = (N+1)x2^M (total divider = 16xk in mode 6b) * +-+-+-+------------------ M (0..15) * | | | | +-+-+-+-+-+-- N (0..63) * 0 0 0 0 | | | | 0 0 | | | | | | * ...-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| lower bits * */static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state){ struct dscc4_dev_priv *dpriv = dscc4_priv(dev); int ret = -1; u32 brr; *state &= ~Ccr0ClockMask; if (*bps) { /* Clock generated - required for DCE */ u32 n = 0, m = 0, divider; int xtal; xtal = dpriv->pci_priv->xtal_hz; if (!xtal) goto done; if (dscc4_check_clock_ability(dpriv->dev_id) < 0) goto done; divider = xtal / *bps; if (divider > BRR_DIVIDER_MAX) { divider >>= 4; *state |= 0x00000036; /* Clock mode 6b (BRG/16) */ } else *state |= 0x00000037; /* Clock mode 7b (BRG) */ if (divider >> 22) { n = 63; m = 15; } else if (divider) { /* Extraction of the 6 highest weighted bits */ m = 0; while (0xffffffc0 & divider) { m++; divider >>= 1; } n = divider; } brr = (m << 8) | n; divider = n << m; if (!(*state & 0x00000001)) /* ?b mode mask => clock mode 6b */ divider <<= 4; *bps = xtal / divider; } else { /* * External clock - DTE * "state" already reflects Clock mode 0a (CCR0 = 0xzzzzzz00). * Nothing more to be done */ brr = 0; } scc_writel(brr, dpriv, dev, BRR); ret = 0;done: return ret;}static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; struct dscc4_dev_priv *dpriv = dscc4_priv(dev); const size_t size = sizeof(dpriv->settings); int ret = 0; if (dev->flags & IFF_UP) return -EBUSY; if (cmd != SIOCWANDEV) return -EOPNOTSUPP; switch(ifr->ifr_settings.type) { case IF_GET_IFACE: ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; if (ifr->ifr_settings.size < size) { ifr->ifr_settings.size = size; /* data size wanted */ return -ENOBUFS; } if (copy_to_user(line, &dpriv->settings, size)) return -EFAULT; break; case IF_IFACE_SYNC_SERIAL: 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); break; default: ret = hdlc_ioctl(dev, ifr, cmd); break; } return ret;}static int dscc4_match(struct thingie *p, int value){ int i; for (i = 0; p[i].define != -1; i++) { if (value == p[i].define) break; } if (p[i].define == -1) return -1; else return i;}static int dscc4_clock_setting(struct dscc4_dev_priv *dpriv, struct net_device *dev){ sync_serial_settings *settings = &dpriv->settings; int ret = -EOPNOTSUPP; u32 bps, state; bps = settings->clock_rate; state = scc_readl(dpriv, CCR0); if (dscc4_set_clock(dev, &bps, &state) < 0) goto done; if (bps) { /* DCE */ printk(KERN_DEBUG "%s: generated RxClk (DCE)\n", dev->name); if (settings->clock_rate != bps) { printk(KERN_DEBUG "%s: clock adjusted (%08d -> %08d)\n", dev->name, settings->clock_rate, bps); settings->clock_rate = bps; } } else { /* DTE */ state |= PowerUp | Vis; printk(KERN_DEBUG "%s: external RxClk (DTE)\n", dev->name); } scc_writel(state, dpriv, dev, CCR0); ret = 0;done: return ret;}static int dscc4_encoding_setting(struct dscc4_dev_priv *dpriv, struct net_device *dev){ struct thingie encoding[] = { { ENCODING_NRZ, 0x00000000 }, { ENCODING_NRZI, 0x00200000 }, { ENCODING_FM_MARK, 0x00400000 }, { ENCODING_FM_SPACE, 0x00500000 }, { ENCODING_MANCHESTER, 0x00600000 }, { -1, 0} }; int i, ret = 0; i = dscc4_match(encoding, dpriv->encoding); if (i >= 0) scc_patchl(EncodingMask, encoding[i].bits, dpriv, dev, CCR0); else ret = -EOPNOTSUPP; return ret;}static int dscc4_loopback_setting(struct dscc4_dev_priv *dpriv, struct net_device *dev){ sync_serial_settings *settings = &dpriv->settings; u32 state; state = scc_readl(dpriv, CCR1); if (settings->loopback) { printk(KERN_DEBUG "%s: loopback\n", dev->name); state |= 0x00000100; } else { printk(KERN_DEBUG "%s: normal\n", dev->name); state &= ~0x00000100; } scc_writel(state, dpriv, dev, CCR1); return 0;}static int dscc4_crc_setting(struct dscc4_dev_priv *dpriv, struct net_device *dev){ struct thingie crc[] = { { PARITY_CRC16_PR0_CCITT, 0x00000010 }, { PARITY_CRC16_PR1_CCITT, 0x00000000 }, { PARITY_CRC32_PR0_CCITT, 0x00000011 }, { PARITY_CRC32_PR1_CCITT, 0x00000001 } }; int i, ret = 0; i = dscc4_match(crc, dpriv->parity); if (i >= 0) scc_patchl(CrcMask, crc[i].bits, dpriv, dev, CCR1); else ret = -EOPNOTSUPP; return ret;}static int dscc4_set_iface(struct dscc4_dev_priv *dpriv, struct net_device *dev){ struct { int (*action)(struct dscc4_dev_priv *, struct net_device *); } *p, do_setting[] = { { dscc4_encoding_setting }, { dscc4_clock_setting }, { dscc4_loopback_setting }, { dscc4_crc_setting }, { NULL } }; int ret = 0; for (p = do_setting; p->action; p++) { if ((ret = p->action(dpriv, dev)) < 0) break; } return ret;}static irqreturn_t dscc4_irq(int irq, void *token, struct pt_regs *ptregs){ struct dscc4_dev_priv *root = token; struct dscc4_pci_priv *priv; struct net_device *dev; void __iomem *ioaddr; u32 state; unsigned long flags; int i, handled = 1; priv = root->pci_priv; dev = dscc4_to_dev(root); spin_lock_irqsave(&priv->lock, flags); ioaddr = root->base_addr; state = readl(ioaddr + GSTAR); if (!state) { handled = 0; goto out; } if (debug > 3) printk(KERN_DEBUG "%s: GSTAR = 0x%08x\n", DRV_NAME, state); writel(state, ioaddr + GSTAR); if (state & Arf) { printk(KERN_ERR "%s: failure (Arf). Harass the maintener\n", dev->name); goto out; } state &= ~ArAck; if (state & Cfg) { if (debug > 0) printk(KERN_DEBUG "%s: CfgIV\n", DRV_NAME); if (priv->iqcfg[priv->cfg_cur++%IRQ_RING_SIZE] & Arf) printk(KERN_ERR "%s: %s failed\n", dev->name, "CFG"); if (!(state &= ~Cfg)) goto out; } if (state & RxEvt) { i = dev_per_card - 1; do { dscc4_rx_irq(priv, root + i); } while (--i >= 0); state &= ~RxEvt; } if (state & TxEvt) { i = dev_per_card - 1; do { dscc4_tx_irq(priv, root + i); } while (--i >= 0); state &= ~TxEvt; }out: spin_unlock_irqrestore(&priv->lock, flags); return IRQ_RETVAL(handled);}static void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_priv *dpriv){ struct net_device *dev = dscc4_to_dev(dpriv); u32 state; int cur, loop = 0;try: cur = dpriv->iqtx_current%IRQ_RING_SIZE; state = dpriv->iqtx[cur]; if (!state) { if (debug > 4) printk(KERN_DEBUG "%s: Tx ISR = 0x%08x\n", dev->name, state); if ((debug > 1) && (loop > 1)) printk(KERN_DEBUG "%s: Tx irq loop=%d\n", dev->name, loop); if (loop && netif_queue_stopped(dev)) if ((dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE) netif_wake_queue(dev); if (netif_running(dev) && dscc4_tx_quiescent(dpriv, dev) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -