diff options
author | Krzysztof Halasa <khc@pm.waw.pl> | 2010-08-12 23:14:07 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-30 17:36:50 -0700 |
commit | 921a86e0e306e42452e16894f2cc792659ede16b (patch) | |
tree | 3c3709b0ad5c9a324db7a56530675b9d9f2198c1 /drivers/staging/sbe-2t3e3/main.c | |
parent | b0b57633089ee4726aefe20760132c41bbd83fff (diff) |
Staging: Add SBE 2T3E3 WAN driver
This is a driver for SBE Inc.'s dual port T3/E3 WAN cards. Based on
their original GPLed driver.
The original driver tarball is now accessible at
http://userweb.kernel.org/~chris/SBE_2T3_Linux_2.0c.tgz
It needs at least a new generic HDLC setup code (not yet written) before
moving to drivers/net/wan.
Signed-off-by: Krzysztof HaĆasa <khc@pm.waw.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/sbe-2t3e3/main.c')
-rw-r--r-- | drivers/staging/sbe-2t3e3/main.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/drivers/staging/sbe-2t3e3/main.c b/drivers/staging/sbe-2t3e3/main.c new file mode 100644 index 000000000000..f3dbef6b0eef --- /dev/null +++ b/drivers/staging/sbe-2t3e3/main.c @@ -0,0 +1,171 @@ +/* + * SBE 2T3E3 synchronous serial card driver for Linux + * + * Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This code is based on a driver written by SBE Inc. + */ + +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include "2t3e3.h" + +void t3e3_init(struct channel *sc) +{ + cpld_init(sc); + dc_reset(sc); + dc_init(sc); + exar7250_init(sc); + exar7300_init(sc); +} + +int t3e3_if_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct channel *sc = dev_to_priv(dev); + u32 current_write, last_write; + unsigned long flags; + struct sk_buff *skb2; + + if (skb == NULL) { + sc->s.out_errors++; + return 0; + } + + if (sc->p.transmitter_on != SBE_2T3E3_ON) { + sc->s.out_errors++; + sc->s.out_dropped++; + dev_kfree_skb_any(skb); + return 0; + } + + if (sc->s.OOF && sc->p.loopback == SBE_2T3E3_LOOPBACK_NONE) { + sc->s.out_dropped++; + dev_kfree_skb_any(skb); + return 0; + } + + spin_lock_irqsave(&sc->ether.tx_lock, flags); + + current_write = sc->ether.tx_ring_current_write; + for (skb2 = skb; skb2 != NULL; skb2 = NULL) { + if (skb2->len) { + if ((sc->ether.tx_ring[current_write].tdes1 & + SBE_2T3E3_TX_DESC_BUFFER_1_SIZE) > 0) + break; + current_write = (current_write + 1) % SBE_2T3E3_TX_DESC_RING_SIZE; + /* + * Leave at least 1 tx desc free so that dc_intr_tx() can + * identify empty list + */ + if (current_write == sc->ether.tx_ring_current_read) + break; + } + } + if (skb2 != NULL) { + netif_stop_queue(sc->dev); + sc->ether.tx_full = 1; + dev_dbg(&sc->pdev->dev, "SBE 2T3E3: out of descriptors\n"); + spin_unlock_irqrestore(&sc->ether.tx_lock, flags); + return NETDEV_TX_BUSY; + } + + current_write = last_write = sc->ether.tx_ring_current_write; + dev_dbg(&sc->pdev->dev, "sending mbuf (current_write = %d)\n", + current_write); + + for (skb2 = skb; skb2 != NULL; skb2 = NULL) { + if (skb2->len) { + dev_dbg(&sc->pdev->dev, + "sending mbuf (len = %d, next = %p)\n", + skb2->len, NULL); + + sc->ether.tx_free_cnt--; + sc->ether.tx_ring[current_write].tdes0 = 0; + sc->ether.tx_ring[current_write].tdes1 &= + SBE_2T3E3_TX_DESC_END_OF_RING | + SBE_2T3E3_TX_DESC_SECOND_ADDRESS_CHAINED; +/* DISABLE_PADDING sometimes gets lost somehow, hands off... */ + sc->ether.tx_ring[current_write].tdes1 |= + SBE_2T3E3_TX_DESC_DISABLE_PADDING | skb2->len; + + if (current_write == sc->ether.tx_ring_current_write) { + sc->ether.tx_ring[current_write].tdes1 |= + SBE_2T3E3_TX_DESC_FIRST_SEGMENT; + } else { + sc->ether.tx_ring[current_write].tdes0 = + SBE_2T3E3_TX_DESC_21143_OWN; + } + + sc->ether.tx_ring[current_write].tdes2 = virt_to_phys(skb2->data); + sc->ether.tx_data[current_write] = NULL; + + last_write = current_write; + current_write = (current_write + 1) % SBE_2T3E3_TX_DESC_RING_SIZE; + } + } + + sc->ether.tx_data[last_write] = skb; + sc->ether.tx_ring[last_write].tdes1 |= + SBE_2T3E3_TX_DESC_LAST_SEGMENT | + SBE_2T3E3_TX_DESC_INTERRUPT_ON_COMPLETION; + sc->ether.tx_ring[sc->ether.tx_ring_current_write].tdes0 |= + SBE_2T3E3_TX_DESC_21143_OWN; + sc->ether.tx_ring_current_write = current_write; + + dev_dbg(&sc->pdev->dev, "txput: tdes0 = %08X tdes1 = %08X\n", + sc->ether.tx_ring[last_write].tdes0, + sc->ether.tx_ring[last_write].tdes1); + + dc_write(sc->addr, SBE_2T3E3_21143_REG_TRANSMIT_POLL_DEMAND, + 0xffffffff); + + spin_unlock_irqrestore(&sc->ether.tx_lock, flags); + return 0; +} + + +void t3e3_read_card_serial_number(struct channel *sc) +{ + u32 i; + + for (i = 0; i < 3; i++) + sc->ether.card_serial_number[i] = t3e3_eeprom_read_word(sc, 10 + i); + + printk(KERN_INFO "SBE wanPMC-2T3E3 serial number: %04X%04X%04X\n", + sc->ether.card_serial_number[0], sc->ether.card_serial_number[1], + sc->ether.card_serial_number[2]); +} + +/* + bit 0 led1 (green) + bit 1 led1 (yellow) + + bit 2 led2 (green) + bit 3 led2 (yellow) + + bit 4 led3 (green) + bit 5 led3 (yellow) + + bit 6 led4 (green) + bit 7 led4 (yellow) +*/ + +void update_led(struct channel *sc, int blinker) +{ + int leds; + if (sc->s.LOS) + leds = 0; /* led1 = off */ + else if (sc->s.OOF) + leds = 2; /* led1 = yellow */ + else if ((blinker & 1) && sc->rcv_count) { + leds = 0; /* led1 = off */ + sc->rcv_count = 0; + } else + leds = 1; /* led1 = green */ + cpld_write(sc, SBE_2T3E3_CPLD_REG_LEDR, leds); + sc->leds = leds; +} |