Release 4.11 drivers/atm/nicstar.c
/*
* nicstar.c
*
* Device driver supporting CBR for IDT 77201/77211 "NICStAR" based cards.
*
* IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME.
* It was taken from the frle-0.22 device driver.
* As the file doesn't have a copyright notice, in the file
* nicstarmac.copyright I put the copyright notice from the
* frle-0.22 device driver.
* Some code is based on the nicstar driver by M. Welsh.
*
* Author: Rui Prior (rprior@inescn.pt)
* PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999
*
*
* (C) INESC 1999
*/
/*
* IMPORTANT INFORMATION
*
* There are currently three types of spinlocks:
*
* 1 - Per card interrupt spinlock (to protect structures and such)
* 2 - Per SCQ scq spinlock
* 3 - Per card resource spinlock (to access registers, etc.)
*
* These must NEVER be grabbed in reverse order.
*
*/
/* Header files */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/atmdev.h>
#include <linux/atm.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/etherdevice.h>
#include "nicstar.h"
#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
#include "suni.h"
#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
#include "idt77105.h"
#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
/* Additional code */
#include "nicstarmac.c"
/* Configurable parameters */
#undef PHY_LOOPBACK
#undef TX_DEBUG
#undef RX_DEBUG
#undef GENERAL_DEBUG
#undef EXTRA_DEBUG
/* Do not touch these */
#ifdef TX_DEBUG
#define TXPRINTK(args...) printk(args)
#else
#define TXPRINTK(args...)
#endif /* TX_DEBUG */
#ifdef RX_DEBUG
#define RXPRINTK(args...) printk(args)
#else
#define RXPRINTK(args...)
#endif /* RX_DEBUG */
#ifdef GENERAL_DEBUG
#define PRINTK(args...) printk(args)
#else
#define PRINTK(args...)
#endif /* GENERAL_DEBUG */
#ifdef EXTRA_DEBUG
#define XPRINTK(args...) printk(args)
#else
#define XPRINTK(args...)
#endif /* EXTRA_DEBUG */
/* Macros */
#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ)
#define NS_DELAY mdelay(1)
#define PTR_DIFF(a, b) ((u32)((unsigned long)(a) - (unsigned long)(b)))
#ifndef ATM_SKB
#define ATM_SKB(s) (&(s)->atm)
#endif
#define scq_virt_to_bus(scq, p) \
(scq->dma + ((unsigned long)(p) - (unsigned long)(scq)->org))
/* Function declarations */
static u32 ns_read_sram(ns_dev * card, u32 sram_address);
static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
int count);
static int ns_init_card(int i, struct pci_dev *pcidev);
static void ns_init_card_error(ns_dev * card, int error);
static scq_info *get_scq(ns_dev *card, int size, u32 scd);
static void free_scq(ns_dev *card, scq_info * scq, struct atm_vcc *vcc);
static void push_rxbufs(ns_dev *, struct sk_buff *);
static irqreturn_t ns_irq_handler(int irq, void *dev_id);
static int ns_open(struct atm_vcc *vcc);
static void ns_close(struct atm_vcc *vcc);
static void fill_tst(ns_dev * card, int n, vc_map * vc);
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb);
static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
struct sk_buff *skb);
static void process_tsq(ns_dev * card);
static void drain_scq(ns_dev * card, scq_info * scq, int pos);
static void process_rsq(ns_dev * card);
static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe);
static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb);
static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count);
static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb);
static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb);
static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb);
static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page);
static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
#ifdef EXTRA_DEBUG
static void which_list(ns_dev * card, struct sk_buff *skb);
#endif
static void ns_poll(unsigned long arg);
static void ns_phy_put(struct atm_dev *dev, unsigned char value,
unsigned long addr);
static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr);
/* Global variables */
static struct ns_dev *cards[NS_MAX_CARDS];
static unsigned num_cards;
static struct atmdev_ops atm_ops = {
.open = ns_open,
.close = ns_close,
.ioctl = ns_ioctl,
.send = ns_send,
.phy_put = ns_phy_put,
.phy_get = ns_phy_get,
.proc_read = ns_proc_read,
.owner = THIS_MODULE,
};
static struct timer_list ns_timer;
static char *mac[NS_MAX_CARDS];
module_param_array(mac, charp, NULL, 0);
MODULE_LICENSE("GPL");
/* Functions */
static int nicstar_init_one(struct pci_dev *pcidev,
const struct pci_device_id *ent)
{
static int index = -1;
unsigned int error;
index++;
cards[index] = NULL;
error = ns_init_card(index, pcidev);
if (error) {
cards[index--] = NULL; /* don't increment index */
goto err_out;
}
return 0;
err_out:
return -ENODEV;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 42 | 56.76% | 1 | 33.33% |
Chas Williams | 31 | 41.89% | 1 | 33.33% |
Christoph Hellwig | 1 | 1.35% | 1 | 33.33% |
Total | 74 | 100.00% | 3 | 100.00% |
static void nicstar_remove_one(struct pci_dev *pcidev)
{
int i, j;
ns_dev *card = pci_get_drvdata(pcidev);
struct sk_buff *hb;
struct sk_buff *iovb;
struct sk_buff *lb;
struct sk_buff *sb;
i = card->index;
if (cards[i] == NULL)
return;
if (card->atmdev->phy && card->atmdev->phy->stop)
card->atmdev->phy->stop(card->atmdev);
/* Stop everything */
writel(0x00000000, card->membase + CFG);
/* De-register device */
atm_dev_deregister(card->atmdev);
/* Disable PCI device */
pci_disable_device(pcidev);
/* Free up resources */
j = 0;
PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) {
dev_kfree_skb_any(hb);
j++;
}
PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
j = 0;
PRINTK("nicstar%d: freeing %d iovec buffers.\n", i,
card->iovpool.count);
while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) {
dev_kfree_skb_any(iovb);
j++;
}
PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
dev_kfree_skb_any(lb);
while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
dev_kfree_skb_any(sb);
free_scq(card, card->scq0, NULL);
for (j = 0; j < NS_FRSCD_NUM; j++) {
if (card->scd2vc[j] != NULL)
free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
}
idr_destroy(&card->idr);
dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT,
card->rsq.org, card->rsq.dma);
dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT,
card->tsq.org, card->tsq.dma);
free_irq(card->pcidev->irq, card);
iounmap(card->membase);
kfree(card);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 329 | 79.47% | 3 | 37.50% |
Chas Williams | 84 | 20.29% | 4 | 50.00% |
Christoph Hellwig | 1 | 0.24% | 1 | 12.50% |
Total | 414 | 100.00% | 8 | 100.00% |
static struct pci_device_id nicstar_pci_tbl[] = {
{ PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77201), 0 },
{0,} /* terminate list */
};
MODULE_DEVICE_TABLE(pci, nicstar_pci_tbl);
static struct pci_driver nicstar_driver = {
.name = "nicstar",
.id_table = nicstar_pci_tbl,
.probe = nicstar_init_one,
.remove = nicstar_remove_one,
};
static int __init nicstar_init(void)
{
unsigned error = 0; /* Initialized to remove compile warning */
XPRINTK("nicstar: nicstar_init() called.\n");
error = pci_register_driver(&nicstar_driver);
TXPRINTK("nicstar: TX debug enabled.\n");
RXPRINTK("nicstar: RX debug enabled.\n");
PRINTK("nicstar: General debug enabled.\n");
#ifdef PHY_LOOPBACK
printk("nicstar: using PHY loopback.\n");
#endif /* PHY_LOOPBACK */
XPRINTK("nicstar: nicstar_init() returned.\n");
if (!error) {
init_timer(&ns_timer);
ns_timer.expires = jiffies + NS_POLL_PERIOD;
ns_timer.data = 0UL;
ns_timer.function = ns_poll;
add_timer(&ns_timer);
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 101 | 100.00% | 2 | 100.00% |
Total | 101 | 100.00% | 2 | 100.00% |
static void __exit nicstar_cleanup(void)
{
XPRINTK("nicstar: nicstar_cleanup() called.\n");
del_timer(&ns_timer);
pci_unregister_driver(&nicstar_driver);
XPRINTK("nicstar: nicstar_cleanup() returned.\n");
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 26 | 83.87% | 1 | 50.00% |
Linus Torvalds (pre-git) | 5 | 16.13% | 1 | 50.00% |
Total | 31 | 100.00% | 2 | 100.00% |
static u32 ns_read_sram(ns_dev * card, u32 sram_address)
{
unsigned long flags;
u32 data;
sram_address <<= 2;
sram_address &= 0x0007FFFC; /* address must be dword aligned */
sram_address |= 0x50000000; /* SRAM read command */
spin_lock_irqsave(&card->res_lock, flags);
while (CMD_BUSY(card)) ;
writel(sram_address, card->membase + CMD);
while (CMD_BUSY(card)) ;
data = readl(card->membase + DR0);
spin_unlock_irqrestore(&card->res_lock, flags);
return data;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 91 | 95.79% | 2 | 66.67% |
Mark Asselstine | 4 | 4.21% | 1 | 33.33% |
Total | 95 | 100.00% | 3 | 100.00% |
static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
int count)
{
unsigned long flags;
int i, c;
count--; /* count range now is 0..3 instead of 1..4 */
c = count;
c <<= 2; /* to use increments of 4 */
spin_lock_irqsave(&card->res_lock, flags);
while (CMD_BUSY(card)) ;
for (i = 0; i <= c; i += 4)
writel(*(value++), card->membase + i);
/* Note: DR# registers are the first 4 dwords in nicstar's memspace,
so card->membase + DR0 == card->membase */
sram_address <<= 2;
sram_address &= 0x0007FFFC;
sram_address |= (0x40000000 | count);
writel(sram_address, card->membase + CMD);
spin_unlock_irqrestore(&card->res_lock, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 122 | 96.06% | 2 | 50.00% |
Mark Asselstine | 4 | 3.15% | 1 | 25.00% |
Chas Williams | 1 | 0.79% | 1 | 25.00% |
Total | 127 | 100.00% | 4 | 100.00% |
static int ns_init_card(int i, struct pci_dev *pcidev)
{
int j;
struct ns_dev *card = NULL;
unsigned char pci_latency;
unsigned error;
u32 data;
u32 u32d[4];
u32 ns_cfg_rctsize;
int bcount;
unsigned long membase;
error = 0;
if (pci_enable_device(pcidev)) {
printk("nicstar%d: can't enable PCI device\n", i);
error = 2;
ns_init_card_error(card, error);
return error;
}
if (dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)) != 0) {
printk(KERN_WARNING
"nicstar%d: No suitable DMA available.\n", i);
error = 2;
ns_init_card_error(card, error);
return error;
}
card = kmalloc(sizeof(*card), GFP_KERNEL);
if (!card) {
printk
("nicstar%d: can't allocate memory for device structure.\n",
i);
error = 2;
ns_init_card_error(card, error);
return error;
}
cards[i] = card;
spin_lock_init(&card->int_lock);
spin_lock_init(&card->res_lock);
pci_set_drvdata(pcidev, card);
card->index = i;
card->atmdev = NULL;
card->pcidev = pcidev;
membase = pci_resource_start(pcidev, 1);
card->membase = ioremap(membase, NS_IOREMAP_SIZE);
if (!card->membase) {
printk("nicstar%d: can't ioremap() membase.\n", i);
error = 3;
ns_init_card_error(card, error);
return error;
}
PRINTK("nicstar%d: membase at 0x%p.\n", i, card->membase);
pci_set_master(pcidev);
if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0) {
printk("nicstar%d: can't read PCI latency timer.\n", i);
error = 6;
ns_init_card_error(card, error);
return error;
}
#ifdef NS_PCI_LATENCY
if (pci_latency < NS_PCI_LATENCY) {
PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i,
NS_PCI_LATENCY);
for (j = 1; j < 4; j++) {
if (pci_write_config_byte
(pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0)
break;
}
if (j == 4) {
printk
("nicstar%d: can't set PCI latency timer to %d.\n",
i, NS_PCI_LATENCY);
error = 7;
ns_init_card_error(card, error);
return error;
}
}
#endif /* NS_PCI_LATENCY */
/* Clear timer overflow */
data = readl(card->membase + STAT);
if (data & NS_STAT_TMROF)
writel(NS_STAT_TMROF, card->membase + STAT);
/* Software reset */
writel(NS_CFG_SWRST, card->membase + CFG);
NS_DELAY;
writel(0x00000000, card->membase + CFG);
/* PHY reset */
writel(0x00000008, card->membase + GP);
NS_DELAY;
writel(0x00000001, card->membase + GP);
NS_DELAY;
while (CMD_BUSY(card)) ;
writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD); /* Sync UTOPIA with SAR clock */
NS_DELAY;
/* Detect PHY type */
while (CMD_BUSY(card)) ;
writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD);
while (CMD_BUSY(card)) ;
data = readl(card->membase + DR0);
switch (data) {
case 0x00000009:
printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
card->max_pcr = ATM_25_PCR;
while (CMD_BUSY(card)) ;
writel(0x00000008, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
/* Clear an eventual pending interrupt */
writel(NS_STAT_SFBQF, card->membase + STAT);
#ifdef PHY_LOOPBACK
while (CMD_BUSY(card)) ;
writel(0x00000022, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
#endif /* PHY_LOOPBACK */
break;
case 0x00000030:
case 0x00000031:
printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
card->max_pcr = ATM_OC3_PCR;
#ifdef PHY_LOOPBACK
while (CMD_BUSY(card)) ;
writel(0x00000002, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
#endif /* PHY_LOOPBACK */
break;
default:
printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data);
error = 8;
ns_init_card_error(card, error);
return error;
}
writel(0x00000000, card->membase + GP);
/* Determine SRAM size */
data = 0x76543210;
ns_write_sram(card, 0x1C003, &data, 1);
data = 0x89ABCDEF;
ns_write_sram(card, 0x14003, &data, 1);
if (ns_read_sram(card, 0x14003) == 0x89ABCDEF &&
ns_read_sram(card, 0x1C003) == 0x76543210)
card->sram_size = 128;
else
card->sram_size = 32;
PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size);
card->rct_size = NS_MAX_RCTSIZE;
#if (NS_MAX_RCTSIZE == 4096)
if (card->sram_size == 128)
printk
("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n",
i);
#elif (NS_MAX_RCTSIZE == 16384)
if (card->sram_size == 32) {
printk
("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n",
i);
card->rct_size = 4096;
}
#else
#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c
#endif
card->vpibits = NS_VPIBITS;
if (card->rct_size == 4096)
card->vcibits = 12 - NS_VPIBITS;
else /* card->rct_size == 16384 */
card->vcibits = 14 - NS_VPIBITS;
/* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */
if (mac[i] == NULL)
nicstar_init_eprom(card->membase);
/* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */
writel(0x00000000, card->membase + VPM);
/* Initialize TSQ */
card->tsq.org = dma_alloc_coherent(&card->pcidev->dev,
NS_TSQSIZE + NS_TSQ_ALIGNMENT,
&card->tsq.dma, GFP_KERNEL);
if (card->tsq.org == NULL) {
printk("nicstar%d: can't allocate TSQ.\n", i);
error = 10;
ns_init_card_error(card, error);
return error;
}
card->tsq.base = PTR_ALIGN(card->tsq.org, NS_TSQ_ALIGNMENT);
card->tsq.next = card->tsq.base;
card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1);
for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++)
ns_tsi_init(card->tsq.base + j);
writel(0x00000000, card->membase + TSQH);
writel(ALIGN(card->tsq.dma, NS_TSQ_ALIGNMENT), card->membase + TSQB);
PRINTK("nicstar%d: TSQ base at 0x%p.\n", i, card->tsq.base);
/* Initialize RSQ */
card->rsq.org = dma_alloc_coherent(&card->pcidev->dev,
NS_RSQSIZE + NS_RSQ_ALIGNMENT,
&card->rsq.dma, GFP_KERNEL);
if (card->rsq.org == NULL) {
printk("nicstar%d: can't allocate RSQ.\n", i);
error = 11;
ns_init_card_error(card, error);
return error;
}
card->rsq.base = PTR_ALIGN(card->rsq.org, NS_RSQ_ALIGNMENT);
card->rsq.next = card->rsq.base;
card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1);
for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++)
ns_rsqe_init(card->rsq.base + j);
writel(0x00000000, card->membase + RSQH);
writel(ALIGN(card->rsq.dma, NS_RSQ_ALIGNMENT), card->membase + RSQB);
PRINTK("nicstar%d: RSQ base at 0x%p.\n", i, card->rsq.base);
/* Initialize SCQ0, the only VBR SCQ used */
card->scq1 = NULL;
card->scq2 = NULL;
card->scq0 = get_scq(card, VBR_SCQSIZE, NS_VRSCD0);
if (card->scq0 == NULL) {
printk("nicstar%d: can't get SCQ0.\n", i);
error = 12;
ns_init_card_error(card, error);
return error;
}
u32d[0] = scq_virt_to_bus(card->scq0, card->scq0->base);
u32d[1] = (u32) 0x00000000;
u32d[2] = (u32) 0xffffffff;
u32d[3] = (u32) 0x00000000;
ns_write_sram(card, NS_VRSCD0, u32d, 4);
ns_write_sram(card, NS_VRSCD1, u32d, 4); /* These last two won't be used */
ns_write_sram(card, NS_VRSCD2, u32d, 4); /* but are initialized, just in case... */
card->scq0->scd = NS_VRSCD0;
PRINTK("nicstar%d: VBR-SCQ0 base at 0x%p.\n", i, card->scq0->base);
/* Initialize TSTs */
card->tst_addr = NS_TST0;
card->tst_free_entries = NS_TST_NUM_ENTRIES;
data = NS_TST_OPCODE_VARIABLE;
for (