summaryrefslogtreecommitdiff
path: root/drivers/staging/sbe-2t3e3/main.c
diff options
context:
space:
mode:
authorKrzysztof Halasa <khc@pm.waw.pl>2010-08-12 23:14:07 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-30 17:36:50 -0700
commit921a86e0e306e42452e16894f2cc792659ede16b (patch)
tree3c3709b0ad5c9a324db7a56530675b9d9f2198c1 /drivers/staging/sbe-2t3e3/main.c
parentb0b57633089ee4726aefe20760132c41bbd83fff (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.c171
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;
+}