diff options
Diffstat (limited to 'drivers/atm/idt77252.c')
-rw-r--r-- | drivers/atm/idt77252.c | 3882 |
1 files changed, 3882 insertions, 0 deletions
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c new file mode 100644 index 00000000000..baaf1a3d224 --- /dev/null +++ b/drivers/atm/idt77252.c @@ -0,0 +1,3882 @@ +/******************************************************************* + * ident "$Id: idt77252.c,v 1.2 2001/11/11 08:13:54 ecd Exp $" + * + * $Author: ecd $ + * $Date: 2001/11/11 08:13:54 $ + * + * Copyright (c) 2000 ATecoM GmbH + * + * The author may be reached at ecd@atecom.com. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *******************************************************************/ +static char const rcsid[] = +"$Id: idt77252.c,v 1.2 2001/11/11 08:13:54 ecd Exp $"; + + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/skbuff.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/netdevice.h> +#include <linux/atmdev.h> +#include <linux/atm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/wait.h> +#include <asm/semaphore.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/atomic.h> +#include <asm/byteorder.h> + +#ifdef CONFIG_ATM_IDT77252_USE_SUNI +#include "suni.h" +#endif /* CONFIG_ATM_IDT77252_USE_SUNI */ + + +#include "idt77252.h" +#include "idt77252_tables.h" + +static unsigned int vpibits = 1; + + +#define CONFIG_ATM_IDT77252_SEND_IDLE 1 + + +/* + * Debug HACKs. + */ +#define DEBUG_MODULE 1 +#undef HAVE_EEPROM /* does not work, yet. */ + +#ifdef CONFIG_ATM_IDT77252_DEBUG +static unsigned long debug = DBG_GENERAL; +#endif + + +#define SAR_RX_DELAY (SAR_CFG_RXINT_NODELAY) + + +/* + * SCQ Handling. + */ +static struct scq_info *alloc_scq(struct idt77252_dev *, int); +static void free_scq(struct idt77252_dev *, struct scq_info *); +static int queue_skb(struct idt77252_dev *, struct vc_map *, + struct sk_buff *, int oam); +static void drain_scq(struct idt77252_dev *, struct vc_map *); +static unsigned long get_free_scd(struct idt77252_dev *, struct vc_map *); +static void fill_scd(struct idt77252_dev *, struct scq_info *, int); + +/* + * FBQ Handling. + */ +static int push_rx_skb(struct idt77252_dev *, + struct sk_buff *, int queue); +static void recycle_rx_skb(struct idt77252_dev *, struct sk_buff *); +static void flush_rx_pool(struct idt77252_dev *, struct rx_pool *); +static void recycle_rx_pool_skb(struct idt77252_dev *, + struct rx_pool *); +static void add_rx_skb(struct idt77252_dev *, int queue, + unsigned int size, unsigned int count); + +/* + * RSQ Handling. + */ +static int init_rsq(struct idt77252_dev *); +static void deinit_rsq(struct idt77252_dev *); +static void idt77252_rx(struct idt77252_dev *); + +/* + * TSQ handling. + */ +static int init_tsq(struct idt77252_dev *); +static void deinit_tsq(struct idt77252_dev *); +static void idt77252_tx(struct idt77252_dev *); + + +/* + * ATM Interface. + */ +static void idt77252_dev_close(struct atm_dev *dev); +static int idt77252_open(struct atm_vcc *vcc); +static void idt77252_close(struct atm_vcc *vcc); +static int idt77252_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int idt77252_send_oam(struct atm_vcc *vcc, void *cell, + int flags); +static void idt77252_phy_put(struct atm_dev *dev, unsigned char value, + unsigned long addr); +static unsigned char idt77252_phy_get(struct atm_dev *dev, unsigned long addr); +static int idt77252_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, + int flags); +static int idt77252_proc_read(struct atm_dev *dev, loff_t * pos, + char *page); +static void idt77252_softint(void *dev_id); + + +static struct atmdev_ops idt77252_ops = +{ + .dev_close = idt77252_dev_close, + .open = idt77252_open, + .close = idt77252_close, + .send = idt77252_send, + .send_oam = idt77252_send_oam, + .phy_put = idt77252_phy_put, + .phy_get = idt77252_phy_get, + .change_qos = idt77252_change_qos, + .proc_read = idt77252_proc_read, + .owner = THIS_MODULE +}; + +static struct idt77252_dev *idt77252_chain = NULL; +static unsigned int idt77252_sram_write_errors = 0; + +/*****************************************************************************/ +/* */ +/* I/O and Utility Bus */ +/* */ +/*****************************************************************************/ + +static void +waitfor_idle(struct idt77252_dev *card) +{ + u32 stat; + + stat = readl(SAR_REG_STAT); + while (stat & SAR_STAT_CMDBZ) + stat = readl(SAR_REG_STAT); +} + +static u32 +read_sram(struct idt77252_dev *card, unsigned long addr) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&card->cmd_lock, flags); + writel(SAR_CMD_READ_SRAM | (addr << 2), SAR_REG_CMD); + waitfor_idle(card); + value = readl(SAR_REG_DR0); + spin_unlock_irqrestore(&card->cmd_lock, flags); + return value; +} + +static void +write_sram(struct idt77252_dev *card, unsigned long addr, u32 value) +{ + unsigned long flags; + + if ((idt77252_sram_write_errors == 0) && + (((addr > card->tst[0] + card->tst_size - 2) && + (addr < card->tst[0] + card->tst_size)) || + ((addr > card->tst[1] + card->tst_size - 2) && + (addr < card->tst[1] + card->tst_size)))) { + printk("%s: ERROR: TST JMP section at %08lx written: %08x\n", + card->name, addr, value); + } + + spin_lock_irqsave(&card->cmd_lock, flags); + writel(value, SAR_REG_DR0); + writel(SAR_CMD_WRITE_SRAM | (addr << 2), SAR_REG_CMD); + waitfor_idle(card); + spin_unlock_irqrestore(&card->cmd_lock, flags); +} + +static u8 +read_utility(void *dev, unsigned long ubus_addr) +{ + struct idt77252_dev *card = dev; + unsigned long flags; + u8 value; + + if (!card) { + printk("Error: No such device.\n"); + return -1; + } + + spin_lock_irqsave(&card->cmd_lock, flags); + writel(SAR_CMD_READ_UTILITY + ubus_addr, SAR_REG_CMD); + waitfor_idle(card); + value = readl(SAR_REG_DR0); + spin_unlock_irqrestore(&card->cmd_lock, flags); + return value; +} + +static void +write_utility(void *dev, unsigned long ubus_addr, u8 value) +{ + struct idt77252_dev *card = dev; + unsigned long flags; + + if (!card) { + printk("Error: No such device.\n"); + return; + } + + spin_lock_irqsave(&card->cmd_lock, flags); + writel((u32) value, SAR_REG_DR0); + writel(SAR_CMD_WRITE_UTILITY + ubus_addr, SAR_REG_CMD); + waitfor_idle(card); + spin_unlock_irqrestore(&card->cmd_lock, flags); +} + +#ifdef HAVE_EEPROM +static u32 rdsrtab[] = +{ + SAR_GP_EECS | SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ + 0, + SAR_GP_EESCLK, /* 0 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */ +}; + +static u32 wrentab[] = +{ + SAR_GP_EECS | SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK /* 0 */ +}; + +static u32 rdtab[] = +{ + SAR_GP_EECS | SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */ +}; + +static u32 wrtab[] = +{ + SAR_GP_EECS | SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + 0, + SAR_GP_EESCLK, /* 0 */ + SAR_GP_EEDO, + SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ + 0, + SAR_GP_EESCLK /* 0 */ +}; + +static u32 clktab[] = +{ + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0, + SAR_GP_EESCLK, + 0 +}; + +static u32 +idt77252_read_gp(struct idt77252_dev *card) +{ + u32 gp; + + gp = readl(SAR_REG_GP); +#if 0 + printk("RD: %s\n", gp & SAR_GP_EEDI ? "1" : "0"); +#endif + return gp; +} + +static void +idt77252_write_gp(struct idt77252_dev *card, u32 value) +{ + unsigned long flags; + +#if 0 + printk("WR: %s %s %s\n", value & SAR_GP_EECS ? " " : "/CS", + value & SAR_GP_EESCLK ? "HIGH" : "LOW ", + value & SAR_GP_EEDO ? "1" : "0"); +#endif + + spin_lock_irqsave(&card->cmd_lock, flags); + waitfor_idle(card); + writel(value, SAR_REG_GP); + spin_unlock_irqrestore(&card->cmd_lock, flags); +} + +static u8 +idt77252_eeprom_read_status(struct idt77252_dev *card) +{ + u8 byte; + u32 gp; + int i, j; + + gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); + + for (i = 0; i < sizeof(rdsrtab)/sizeof(rdsrtab[0]); i++) { + idt77252_write_gp(card, gp | rdsrtab[i]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + byte = 0; + for (i = 0, j = 0; i < 8; i++) { + byte <<= 1; + + idt77252_write_gp(card, gp | clktab[j++]); + udelay(5); + + byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0; + + idt77252_write_gp(card, gp | clktab[j++]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + return byte; +} + +static u8 +idt77252_eeprom_read_byte(struct idt77252_dev *card, u8 offset) +{ + u8 byte; + u32 gp; + int i, j; + + gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); + + for (i = 0; i < sizeof(rdtab)/sizeof(rdtab[0]); i++) { + idt77252_write_gp(card, gp | rdtab[i]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + for (i = 0, j = 0; i < 8; i++) { + idt77252_write_gp(card, gp | clktab[j++] | + (offset & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + idt77252_write_gp(card, gp | clktab[j++] | + (offset & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + offset >>= 1; + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + byte = 0; + for (i = 0, j = 0; i < 8; i++) { + byte <<= 1; + + idt77252_write_gp(card, gp | clktab[j++]); + udelay(5); + + byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0; + + idt77252_write_gp(card, gp | clktab[j++]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + return byte; +} + +static void +idt77252_eeprom_write_byte(struct idt77252_dev *card, u8 offset, u8 data) +{ + u32 gp; + int i, j; + + gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); + + for (i = 0; i < sizeof(wrentab)/sizeof(wrentab[0]); i++) { + idt77252_write_gp(card, gp | wrentab[i]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + for (i = 0; i < sizeof(wrtab)/sizeof(wrtab[0]); i++) { + idt77252_write_gp(card, gp | wrtab[i]); + udelay(5); + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + for (i = 0, j = 0; i < 8; i++) { + idt77252_write_gp(card, gp | clktab[j++] | + (offset & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + idt77252_write_gp(card, gp | clktab[j++] | + (offset & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + offset >>= 1; + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + + for (i = 0, j = 0; i < 8; i++) { + idt77252_write_gp(card, gp | clktab[j++] | + (data & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + idt77252_write_gp(card, gp | clktab[j++] | + (data & 1 ? SAR_GP_EEDO : 0)); + udelay(5); + + data >>= 1; + } + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); +} + +static void +idt77252_eeprom_init(struct idt77252_dev *card) +{ + u32 gp; + + gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); + + idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK); + udelay(5); + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); + idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK); + udelay(5); + idt77252_write_gp(card, gp | SAR_GP_EECS); + udelay(5); +} +#endif /* HAVE_EEPROM */ + + +#ifdef CONFIG_ATM_IDT77252_DEBUG +static void +dump_tct(struct idt77252_dev *card, int index) +{ + unsigned long tct; + int i; + + tct = (unsigned long) (card->tct_base + index * SAR_SRAM_TCT_SIZE); + + printk("%s: TCT %x:", card->name, index); + for (i = 0; i < 8; i++) { + printk(" %08x", read_sram(card, tct + i)); + } + printk("\n"); +} + +static void +idt77252_tx_dump(struct idt77252_dev *card) +{ + struct atm_vcc *vcc; + struct vc_map *vc; + int i; + + printk("%s\n", __FUNCTION__); + for (i = 0; i < card->tct_size; i++) { + vc = card->vcs[i]; + if (!vc) + continue; + + vcc = NULL; + if (vc->rx_vcc) + vcc = vc->rx_vcc; + else if (vc->tx_vcc) + vcc = vc->tx_vcc; + + if (!vcc) + continue; + + printk("%s: Connection %d:\n", card->name, vc->index); + dump_tct(card, vc->index); + } +} +#endif + + +/*****************************************************************************/ +/* */ +/* SCQ Handling */ +/* */ +/*****************************************************************************/ + +static int +sb_pool_add(struct idt77252_dev *card, struct sk_buff *skb, int queue) +{ + struct sb_pool *pool = &card->sbpool[queue]; + int index; + + index = pool->index; + while (pool->skb[index]) { + index = (index + 1) & FBQ_MASK; + if (index == pool->index) + return -ENOBUFS; + } + + pool->skb[index] = skb; + IDT77252_PRV_POOL(skb) = POOL_HANDLE(queue, index); + + pool->index = (index + 1) & FBQ_MASK; + return 0; +} + +static void +sb_pool_remove(struct idt77252_dev *card, struct sk_buff *skb) +{ + unsigned int queue, index; + u32 handle; + + handle = IDT77252_PRV_POOL(skb); + + queue = POOL_QUEUE(handle); + if (queue > 3) + return; + + index = POOL_INDEX(handle); + if (index > FBQ_SIZE - 1) + return; + + card->sbpool[queue].skb[index] = NULL; +} + +static struct sk_buff * +sb_pool_skb(struct idt77252_dev *card, u32 handle) +{ + unsigned int queue, index; + + queue = POOL_QUEUE(handle); + if (queue > 3) + return NULL; + + index = POOL_INDEX(handle); + if (index > FBQ_SIZE - 1) + return NULL; + + return card->sbpool[queue].skb[index]; +} + +static struct scq_info * +alloc_scq(struct idt77252_dev *card, int class) +{ + struct scq_info *scq; + + scq = (struct scq_info *) kmalloc(sizeof(struct scq_info), GFP_KERNEL); + if (!scq) + return NULL; + memset(scq, 0, sizeof(struct scq_info)); + + scq->base = pci_alloc_consistent(card->pcidev, SCQ_SIZE, + &scq->paddr); + if (scq->base == NULL) { + kfree(scq); + return NULL; + } + memset(scq->base, 0, SCQ_SIZE); + + scq->next = scq->base; + scq->last = scq->base + (SCQ_ENTRIES - 1); + atomic_set(&scq->used, 0); + + spin_lock_init(&scq->lock); + spin_lock_init(&scq->skblock); + + skb_queue_head_init(&scq->transmit); + skb_queue_head_init(&scq->pending); + + TXPRINTK("idt77252: SCQ: base 0x%p, next 0x%p, last 0x%p, paddr %08llx\n", + scq->base, scq->next, scq->last, (unsigned long long)scq->paddr); + + return scq; +} + +static void +free_scq(struct idt77252_dev *card, struct scq_info *scq) +{ + struct sk_buff *skb; + struct atm_vcc *vcc; + + pci_free_consistent(card->pcidev, SCQ_SIZE, + scq->base, scq->paddr); + + while ((skb = skb_dequeue(&scq->transmit))) { + pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->len, PCI_DMA_TODEVICE); + + vcc = ATM_SKB(skb)->vcc; + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); + } + + while ((skb = skb_dequeue(&scq->pending))) { + pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->len, PCI_DMA_TODEVICE); + + vcc = ATM_SKB(skb)->vcc; + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); + } + + kfree(scq); +} + + +static int +push_on_scq(struct idt77252_dev *card, struct vc_map *vc, struct sk_buff *skb) +{ + struct scq_info *scq = vc->scq; + unsigned long flags; + struct scqe *tbd; + int entries; + + TXPRINTK("%s: SCQ: next 0x%p\n", card->name, scq->next); + + atomic_inc(&scq->used); + entries = atomic_read(&scq->used); + if (entries > (SCQ_ENTRIES - 1)) { + atomic_dec(&scq->used); + goto out; + } + + skb_queue_tail(&scq->transmit, skb); + + spin_lock_irqsave(&vc->lock, flags); + if (vc->estimator) { + struct atm_vcc *vcc = vc->tx_vcc; + struct sock *sk = sk_atm(vcc); + + vc->estimator->cells += (skb->len + 47) / 48; + if (atomic_read(&sk->sk_wmem_alloc) > + (sk->sk_sndbuf >> 1)) { + u32 cps = vc->estimator->maxcps; + + vc->estimator->cps = cps; + vc->estimator->avcps = cps << 5; + if (vc->lacr < vc->init_er) { + vc->lacr = vc->init_er; + writel(TCMDQ_LACR | (vc->lacr << 16) | + vc->index, SAR_REG_TCMDQ); + } + } + } + spin_unlock_irqrestore(&vc->lock, flags); + + tbd = &IDT77252_PRV_TBD(skb); + + spin_lock_irqsave(&scq->lock, flags); + scq->next->word_1 = cpu_to_le32(tbd->word_1 | + SAR_TBD_TSIF | SAR_TBD_GTSI); + scq->next->word_2 = cpu_to_le32(tbd->word_2); + scq->next->word_3 = cpu_to_le32(tbd->word_3); + scq->next->word_4 = cpu_to_le32(tbd->word_4); + + if (scq->next == scq->last) + scq->next = scq->base; + else + scq->next++; + + write_sram(card, scq->scd, + scq->paddr + + (u32)((unsigned long)scq->next - (unsigned long)scq->base)); + spin_unlock_irqrestore(&scq->lock, flags); + + scq->trans_start = jiffies; + + if (test_and_clear_bit(VCF_IDLE, &vc->flags)) { + writel(TCMDQ_START_LACR | (vc->lacr << 16) | vc->index, + SAR_REG_TCMDQ); + } + + TXPRINTK("%d entries in SCQ used (push).\n", atomic_read(&scq->used)); + + XPRINTK("%s: SCQ (after push %2d) head = 0x%x, next = 0x%p.\n", + card->name, atomic_read(&scq->used), + read_sram(card, scq->scd + 1), scq->next); + + return 0; + +out: + if (jiffies - scq->trans_start > HZ) { + printk("%s: Error pushing TBD for %d.%d\n", + card->name, vc->tx_vcc->vpi, vc->tx_vcc->vci); +#ifdef CONFIG_ATM_IDT77252_DEBUG + idt77252_tx_dump(card); +#endif + scq->trans_start = jiffies; + } + + return -ENOBUFS; +} + + +static void +drain_scq(struct idt77252_dev *card, struct vc_map *vc) +{ + struct scq_info *scq = vc->scq; + struct sk_buff *skb; + struct atm_vcc *vcc; + + TXPRINTK("%s: SCQ (before drain %2d) next = 0x%p.\n", + card->name, atomic_read(&scq->used), scq->next); + + skb = skb_dequeue(&scq->transmit); + if (skb) { + TXPRINTK("%s: freeing skb at %p.\n", card->name, skb); + + pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->len, PCI_DMA_TODEVICE); + + vcc = ATM_SKB(skb)->vcc; + + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); + + atomic_inc(&vcc->stats->tx); + } + + atomic_dec(&scq->used); + + spin_lock(&scq->skblock); + while ((skb = skb_dequeue(&scq->pending))) { + if (push_on_scq(card, vc, skb)) { + skb_queue_head(&vc->scq->pending, skb); + break; + } + } + spin_unlock(&scq->skblock); +} + +static int +queue_skb(struct idt77252_dev *card, struct vc_map *vc, + struct sk_buff *skb, int oam) +{ + struct atm_vcc *vcc; + struct scqe *tbd; + unsigned long flags; + int error; + int aal; + + if (skb->len == 0) { + printk("%s: invalid skb->len (%d)\n", card->name, skb->len); + return -EINVAL; + } + + TXPRINTK("%s: Sending %d bytes of data.\n", + card->name, skb->len); + + tbd = &IDT77252_PRV_TBD(skb); + vcc = ATM_SKB(skb)->vcc; + + IDT77252_PRV_PADDR(skb) = pci_map_single(card->pcidev, skb->data, + skb->len, PCI_DMA_TODEVICE); + + error = -EINVAL; + + if (oam) { + if (skb->len != 52) + goto errout; + + tbd->word_1 = SAR_TBD_OAM | ATM_CELL_PAYLOAD | SAR_TBD_EPDU; + tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4; + tbd->word_3 = 0x00000000; + tbd->word_4 = (skb->data[0] << 24) | (skb->data[1] << 16) | + (skb->data[2] << 8) | (skb->data[3] << 0); + + if (test_bit(VCF_RSV, &vc->flags)) + vc = card->vcs[0]; + + goto done; + } + + if (test_bit(VCF_RSV, &vc->flags)) { + printk("%s: Trying to transmit on reserved VC\n", card->name); + goto errout; + } + + aal = vcc->qos.aal; + + switch (aal) { + case ATM_AAL0: + case ATM_AAL34: + if (skb->len > 52) + goto errout; + + if (aal == ATM_AAL0) + tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL0 | + ATM_CELL_PAYLOAD; + else + tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL34 | + ATM_CELL_PAYLOAD; + + tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4; + tbd->word_3 = 0x00000000; + tbd->word_4 = (skb->data[0] << 24) | (skb->data[1] << 16) | + (skb->data[2] << 8) | (skb->data[3] << 0); + break; + + case ATM_AAL5: + tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL5 | skb->len; + tbd->word_2 = IDT77252_PRV_PADDR(skb); + tbd->word_3 = skb->len; + tbd->word_4 = (vcc->vpi << SAR_TBD_VPI_SHIFT) | + (vcc->vci << SAR_TBD_VCI_SHIFT); + break; + + case ATM_AAL1: + case ATM_AAL2: + default: + printk("%s: Traffic type not supported.\n", card->name); + error = -EPROTONOSUPPORT; + goto errout; + } + +done: + spin_lock_irqsave(&vc->scq->skblock, flags); + skb_queue_tail(&vc->scq->pending, skb); + + while ((skb = skb_dequeue(&vc->scq->pending))) { + if (push_on_scq(card, vc, skb)) { + skb_queue_head(&vc->scq->pending, skb); + break; + } + } + spin_unlock_irqrestore(&vc->scq->skblock, flags); + + return 0; + +errout: + pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->len, PCI_DMA_TODEVICE); + return error; +} + +static unsigned long +get_free_scd(struct idt77252_dev *card, struct vc_map *vc) +{ + int i; + + for (i = 0; i < card->scd_size; i++) { + if (!card->scd2vc[i]) { + card->scd2vc[i] = vc; + vc->scd_index = i; + return card->scd_base + i * SAR_SRAM_SCD_SIZE; + } + } + return 0; +} + +static void +fill_scd(struct idt77252_dev *card, struct scq_info *scq, int class) +{ + write_sram(card, scq->scd, scq->paddr); + write_sram(card, scq->scd + 1, 0x00000000); + write_sram(card, scq->scd + 2, 0xffffffff); + write_sram(card, scq->scd + 3, 0x00000000); +} + +static void +clear_scd(struct idt77252_dev *card, struct scq_info *scq, int class) +{ + return; +} + +/*****************************************************************************/ +/* */ +/* RSQ Handling */ +/* */ +/*****************************************************************************/ + +static int +init_rsq(struct idt77252_dev *card) +{ + struct rsq_entry *rsqe; + + card->rsq.base = pci_alloc_consistent(card->pcidev, RSQSIZE, + &card->rsq.paddr); + if (card->rsq.base == NULL) { + printk("%s: can't allocate RSQ.\n", card->name); + return -1; + } + memset(card->rsq.base, 0, RSQSIZE); + + card->rsq.last = card->rsq.base + RSQ_NUM_ENTRIES - 1; + card->rsq.next = card->rsq.last; + for (rsqe = card->rsq.base; rsqe <= card->rsq.last; rsqe++) + rsqe->word_4 = 0; + + writel((unsigned long) card->rsq.last - (unsigned long) card->rsq.base, + SAR_REG_RSQH); + writel(card->rsq.paddr, SAR_REG_RSQB); + + IPRINTK("%s: RSQ base at 0x%lx (0x%x).\n", card->name, + (unsigned long) card->rsq.base, + readl(SAR_REG_RSQB)); + IPRINTK("%s: RSQ head = 0x%x, base = 0x%x, tail = 0x%x.\n", + card->name, + readl(SAR_REG_RSQH), + readl(SAR_REG_RSQB), + readl(SAR_REG_RSQT)); + + return 0; +} + +static void +deinit_rsq(struct idt77252_dev *card) +{ + pci_free_consistent(card->pcidev, RSQSIZE, + card->rsq.base, card->rsq.paddr); +} + +static void +dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) +{ + struct atm_vcc *vcc; + struct sk_buff *skb; + struct rx_pool *rpp; + struct vc_map *vc; + u32 header, vpi, vci; + u32 stat; + int i; + + stat = le32_to_cpu(rsqe->word_4); + + if (stat & SAR_RSQE_IDLE) { + RXPRINTK("%s: message about inactive connection.\n", + card->name); + return; + } + + skb = sb_pool_skb(card, le32_to_cpu(rsqe->word_2)); + if (skb == NULL) { + printk("%s: NULL skb in %s, rsqe: %08x %08x %08x %08x\n", + card->name, __FUNCTION__, + le32_to_cpu(rsqe->word_1), le32_to_cpu(rsqe->word_2), + le32_to_cpu(rsqe->word_3), le32_to_cpu(rsqe->word_4)); + return; + } + + header = le32_to_cpu(rsqe->word_1); + vpi = (header >> 16) & 0x00ff; + vci = (header >> 0) & 0xffff; + + RXPRINTK("%s: SDU for %d.%d received in buffer 0x%p (data 0x%p).\n", + card->name, vpi, vci, skb, skb->data); + + if ((vpi >= (1 << card->vpibits)) || (vci != (vci & card->vcimask))) { + printk("%s: SDU received for out-of-range vc %u.%u\n", + card->name, vpi, vci); + recycle_rx_skb(card, skb); + return; + } + + vc = card->vcs[VPCI2VC(card, vpi, vci)]; + if (!vc || !test_bit(VCF_RX, &vc->flags)) { + printk("%s: SDU received on non RX vc %u.%u\n", + card->name, vpi, vci); + recycle_rx_skb(card, skb); + return; + } + + vcc = vc->rx_vcc; + + pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->end - skb->data, PCI_DMA_FROMDEVICE); + + if ((vcc->qos.aal == ATM_AAL0) || + (vcc->qos.aal == ATM_AAL34)) { + struct sk_buff *sb; + unsigned char *cell; + u32 aal0; + + cell = skb->data; + for (i = (stat & SAR_RSQE_CELLCNT); i; i--) { + if ((sb = dev_alloc_skb(64)) == NULL) { + printk("%s: Can't allocate buffers for aal0.\n", + card->name); + atomic_add(i, &vcc->stats->rx_drop); + break; + } + if (!atm_charge(vcc, sb->truesize)) { + RXPRINTK("%s: atm_charge() dropped aal0 packets.\n", + card->name); + atomic_add(i - 1, &vcc->stats->rx_drop); + dev_kfree_skb(sb); + break; + } + aal0 = (vpi << ATM_HDR_VPI_SHIFT) | + (vci << ATM_HDR_VCI_SHIFT); + aal0 |= (stat & SAR_RSQE_EPDU) ? 0x00000002 : 0; + aal0 |= (stat & SAR_RSQE_CLP) ? 0x00000001 : 0; + + *((u32 *) sb->data) = aal0; + skb_put(sb, sizeof(u32)); + memcpy(skb_put(sb, ATM_CELL_PAYLOAD), + cell, ATM_CELL_PAYLOAD); + + ATM_SKB(sb)->vcc = vcc; + do_gettimeofday(&sb->stamp); + vcc->push(vcc, sb); + atomic_inc(&vcc->stats->rx); + + cell += ATM_CELL_PAYLOAD; + } + + recycle_rx_skb(card, skb); + return; + } + if (vcc->qos.aal != ATM_AAL5) { + printk("%s: Unexpected AAL type in dequeue_rx(): %d.\n", + card->name, vcc->qos.aal); + recycle_rx_skb(card, skb); + return; + } + skb->len = (stat & SAR_RSQE_CELLCNT) * ATM_CELL_PAYLOAD; + + rpp = &vc->rcv.rx_pool; + + rpp->len += skb->len; + if (!rpp->count++) + rpp->first = skb; + *rpp->last = skb; + rpp->last = &skb->next; + + if (stat & SAR_RSQE_EPDU) { + unsigned char *l1l2; + unsigned int len; + + l1l2 = (unsigned char *) ((unsigned long) skb->data + skb->len - 6); + + len = (l1l2[0] << 8) | l1l2[1]; + len = len ? len : 0x10000; + + RXPRINTK("%s: PDU has %d bytes.\n", card->name, len); + + if ((len + 8 > rpp->len) || (len + (47 + 8) < rpp->len)) { + RXPRINTK("%s: AAL5 PDU size mismatch: %d != %d. " + "(CDC: %08x)\n", + card->name, len, rpp->len, readl(SAR_REG_CDC)); + recycle_rx_pool_skb(card, rpp); + atomic_inc(&vcc->stats->rx_err); + return; + } + if (stat & SAR_RSQE_CRC) { + RXPRINTK("%s: AAL5 CRC error.\n", card->name); + recycle_rx_pool_skb(card, rpp); + atomic_inc(&vcc->stats->rx_err); + return; + } + if (rpp->count > 1) { + struct sk_buff *sb; + + skb = dev_alloc_skb(rpp->len); + if (!skb) { + RXPRINTK("%s: Can't alloc RX skb.\n", + card->name); + recycle_rx_pool_skb(card, rpp); + atomic_inc(&vcc->stats->rx_err); + return; + } + if (!atm_charge(vcc, skb->truesize)) { + recycle_rx_pool_skb(card, rpp); + dev_kfree_skb(skb); + return; + } + sb = rpp->first; + for (i = 0; i < rpp->count; i++) { + memcpy(skb_put(skb, sb->len), + sb->data, sb->len); + sb = sb->next; + } + + recycle_rx_pool_skb(card, rpp); + + skb_trim(skb, len); + ATM_SKB(skb)->vcc = vcc; + do_gettimeofday(&skb->stamp); + + vcc->push(vcc, skb); + atomic_inc(&vcc->stats->rx); + + return; + } + + skb->next = NULL; + flush_rx_pool(card, rpp); + + if (!atm_charge(vcc, skb->truesize)) { + recycle_rx_skb(card, skb); + return; + } + + pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + skb->end - skb->data, PCI_DMA_FROMDEVICE); + sb_pool_remove(card, skb); + + skb_trim(skb, len); + ATM_SKB(skb)->vcc = vcc; + do_gettimeofday(&skb->stamp); + + vcc->push(vcc, skb); + atomic_inc(&vcc->stats->rx); + + if (skb->truesize > SAR_FB_SIZE_3) + add_rx_skb(card, 3, SAR_FB_SIZE_3, 1); + else if (skb->truesize > SAR_FB_SIZE_2) + add_rx_skb(card, 2, SAR_FB_SIZE_2, 1); + else if (skb->truesize > SAR_FB_SIZE_1) + add_rx_skb(card, 1, SAR_FB_SIZE_1, 1); + else + add_rx_skb(card, 0, SAR_FB_SIZE_0, 1); + return; + } +} + +static void +idt77252_rx(struct idt77252_dev *card) +{ + struct rsq_entry *rsqe; + + if (card->rsq.next == card->rsq.last) + rsqe = card->rsq.base; + else + rsqe = card->rsq.next + 1; + + if (!(le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID)) { + RXPRINTK("%s: no entry in RSQ.\n", card->name); + return; + } + + do { + dequeue_rx(card, rsqe); + rsqe->word_4 = 0; + card->rsq.next = rsqe; + if (card->rsq.next == card->rsq.last) + rsqe = card->rsq.base; + else + rsqe = card->rsq.next + 1; + } while (le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID); + + writel((unsigned long) card->rsq.next - (unsigned long) card->rsq.base, + SAR_REG_RSQH); +} + +static void +idt77252_rx_raw(struct idt77252_dev *card) |