[PATCH] 2.4.20-pre8-ac3 - drivers/atm/iphase update

Francois Romieu (romieu@cogenit.fr)
Sun, 13 Oct 2002 00:04:24 +0200


Greetings,

Content: DMA api update + pci init style + misc fixes. Detail follows.

Tested in 2.4.20-pre1-ac1 with dumb br2684 bridge between two facing 5575
(one on SMP) + multiple short sized ping -f for a total of ~13Mirq. Did
something like ~2k insmod/rmmod without noticeable side effect.
It's satursday evening anyway; if people don't enjoy their atm experience,
the patch is available splitted in three readable parts for easier review
at http://www.cogenit.fr//linux/patches/2.4.20-pre8-ac3/.

I'll diff against 2.5.x soon (TM).

- pci_find_device() removal
- unbalanced atm_dev_register() in kmalloc() failure path fixed
- pci_disable_device() added
- device id for revision 9 of chipset
- pci_{alloc/free}_consistent usage
- unaligned address of allocated memory for descriptor wasn't stored
(actually aligned part wasn't always freed at removal time)
- some more memory leaks on failure paths/device removal fixed
- ia_free_{rx/tx}() helpers so as to avoid code duplication
- pci_{map/unmap}_single usage;
- extra structure to store the bus address of the trailing cpcs data;
- GFP_KERNEL -> GFP_ATOMIC for kmalloc in ia_enque_rtn_q() as it can
be called with spinlock held.

--- linux-patch-2.4.20-pre8-ac3.orig/drivers/atm/iphase.h Sun Feb 4 19:05:29 2001
+++ linux-patch-2.4.20-pre8-ac3/drivers/atm/iphase.h Sat Oct 12 23:21:57 2002
@@ -200,7 +200,13 @@ struct cpcs_trailer
u_short length;
u_int crc32;
};
-
+
+struct cpcs_trailer_desc
+{
+ struct cpcs_trailer *cpcs;
+ dma_addr_t dma_addr;
+};
+
struct ia_vcc
{
int rxing;
@@ -1017,7 +1023,7 @@ typedef struct iadev_t {
struct wait_queue *close_wait;
struct wait_queue *timeout_wait;
#endif
- caddr_t *tx_buf;
+ struct cpcs_trailer_desc *tx_buf;
u16 num_tx_desc, tx_buf_sz, rate_limit;
u32 tx_cell_cnt, tx_pkt_cnt;
u32 MAIN_VC_TABLE_ADDR, EXT_VC_TABLE_ADDR, ABR_SCHED_TABLE_ADDR;
--- linux-patch-2.4.20-pre8-ac3.orig/drivers/atm/iphase.c Sat Oct 12 22:36:01 2002
+++ linux-patch-2.4.20-pre8-ac3/drivers/atm/iphase.c Sat Oct 12 23:21:57 2002
@@ -124,7 +124,7 @@ static void ia_enque_head_rtn_q (IARTN_Q
}

static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) {
- IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) return -1;
entry->data = data;
entry->next = NULL;
@@ -629,6 +629,7 @@ static int ia_que_tx (IADEV *iadev) {
struct ia_vcc *iavcc;
static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb);
num_desc = ia_avail_descs(iadev);
+
while (num_desc && (skb = skb_dequeue(&iadev->tx_backlog))) {
if (!(vcc = ATM_SKB(skb)->vcc)) {
dev_kfree_skb_any(skb);
@@ -648,6 +649,7 @@ static int ia_que_tx (IADEV *iadev) {
}
return 0;
}
+
void ia_tx_poll (IADEV *iadev) {
struct atm_vcc *vcc = NULL;
struct sk_buff *skb = NULL, *skb1 = NULL;
@@ -1091,6 +1093,7 @@ static int rx_pkt(struct atm_dev *dev)
int len;
struct sk_buff *skb;
u_int buf_addr, dma_addr;
+
iadev = INPH_IA_DEV(dev);
if (iadev->rfL.pcq_rd == (readw(iadev->reass_reg+PCQ_WR_PTR)&0xffff))
{
@@ -1192,7 +1195,8 @@ static int rx_pkt(struct atm_dev *dev)

/* Build the DLE structure */
wr_ptr = iadev->rx_dle_q.write;
- wr_ptr->sys_pkt_addr = virt_to_bus(skb->data);
+ wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data,
+ len, PCI_DMA_FROMDEVICE);
wr_ptr->local_pkt_addr = buf_addr;
wr_ptr->bytes = len; /* We don't know this do we ?? */
wr_ptr->mode = DMA_INT_ENABLE;
@@ -1302,7 +1306,7 @@ static void rx_dle_intr(struct atm_dev *
free_desc(dev, desc);

if (!(len = skb->len))
- {
+ {
printk("rx_dle_intr: skb len 0\n");
dev_kfree_skb_any(skb);
}
@@ -1311,6 +1315,9 @@ static void rx_dle_intr(struct atm_dev *
struct cpcs_trailer *trailer;
u_short length;
struct ia_vcc *ia_vcc;
+
+ pci_unmap_single(iadev->pci, iadev->rx_dle_q.write->sys_pkt_addr,
+ len, PCI_DMA_FROMDEVICE);
/* no VCC related housekeeping done as yet. lets see */
vcc = ATM_SKB(skb)->vcc;
if (!vcc) {
@@ -1430,7 +1437,7 @@ static int rx_init(struct atm_dev *dev)
IADEV *iadev;
struct rx_buf_desc *buf_desc_ptr;
unsigned long rx_pkt_start = 0;
- u32 *odle_addr, *dle_addr;
+ void *dle_addr;
struct abr_vc_table *abr_vc_table;
u16 *vc_table;
u16 *reass_table;
@@ -1441,25 +1448,14 @@ static int rx_init(struct atm_dev *dev)

iadev = INPH_IA_DEV(dev);
// spin_lock_init(&iadev->rx_lock);
- /* I need to initialize the DLEs somewhere. Lets see what I
- need to do for this, hmmm...
- - allocate memory for 256 DLEs. make sure that it starts
- on a 4k byte address boundary. Program the start address
- in Receive List address register. ..... to do for TX also
- To make sure that it is a 4k byte boundary - allocate 8k and find
- 4k byte boundary within.
- ( (addr + (4k-1)) & ~(4k-1) )
- */

- /* allocate 8k bytes */
- odle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL);
- if (!odle_addr)
- {
- printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
- return -ENOMEM;
- }
- /* find 4k byte boundary within the 8k allocated */
- dle_addr = (u32*)( ((u32)odle_addr+(4096-1)) & ~(4096-1) );
+ /* Allocate 4k bytes - more aligned than needed (4k boundary) */
+ dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE,
+ &iadev->rx_dle_dma);
+ if (!dle_addr) {
+ printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+ goto err_out;
+ }
iadev->rx_dle_q.start = (struct dle*)dle_addr;
iadev->rx_dle_q.read = iadev->rx_dle_q.start;
iadev->rx_dle_q.write = iadev->rx_dle_q.start;
@@ -1468,7 +1464,8 @@ static int rx_init(struct atm_dev *dev)
DLE that can be used. */

/* write the upper 20 bits of the start address to rx list address register */
- writel(virt_to_bus(dle_addr) & 0xfffff000, iadev->dma+IPHASE5575_RX_LIST_ADDR);
+ writel(iadev->rx_dle_dma & 0xfffff000,
+ iadev->dma + IPHASE5575_RX_LIST_ADDR);
IF_INIT(printk("Tx Dle list addr: 0x%08x value: 0x%0x\n",
(u32)(iadev->dma+IPHASE5575_TX_LIST_ADDR),
*(u32*)(iadev->dma+IPHASE5575_TX_LIST_ADDR));
@@ -1642,8 +1639,7 @@ static int rx_init(struct atm_dev *dev)
{
printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n",
dev->number);
- kfree(odle_addr);
- return -ENOMEM;
+ goto err_free_dle;
}
memset(iadev->rx_open, 0, 4*iadev->num_vc);
iadev->rxing = 1;
@@ -1651,6 +1647,12 @@ static int rx_init(struct atm_dev *dev)
/* Mode Register */
writew(R_ONLINE, iadev->reass_reg+MODE_REG);
return 0;
+
+err_free_dle:
+ pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+ iadev->rx_dle_dma);
+err_out:
+ return -ENOMEM;
}


@@ -1715,6 +1717,12 @@ static void tx_dle_intr(struct atm_dev *
/* free the DMAed skb */
skb = skb_dequeue(&iadev->tx_dma_q);
if (!skb) break;
+
+ /* Revenge of the 2 dle (skb + trailer) used in ia_pkt_tx() */
+ if (!((dle - iadev->tx_dle_q.start)%(2*sizeof(struct dle)))) {
+ pci_unmap_single(iadev->pci, dle->sys_pkt_addr, skb->len,
+ PCI_DMA_TODEVICE);
+ }
vcc = ATM_SKB(skb)->vcc;
if (!vcc) {
printk("tx_dle_intr: vcc is null\n");
@@ -1740,7 +1748,7 @@ static void tx_dle_intr(struct atm_dev *
}
}
else { /* Hold the rate-limited skb for flow control */
- IA_SKB_STATE(skb) |= IA_DLED;
+ IA_SKB_STATE(skb) |= IA_DLED;
skb_queue_tail(&iavcc->txing_skb, skb);
}
IF_EVENT(printk("tx_dle_intr: enque skb = 0x%x \n", (u32)skb);)
@@ -1907,7 +1915,7 @@ static int tx_init(struct atm_dev *dev)
IADEV *iadev;
struct tx_buf_desc *buf_desc_ptr;
unsigned int tx_pkt_start;
- u32 *dle_addr;
+ void *dle_addr;
int i;
u_short tcq_st_adr;
u_short *tcq_start;
@@ -1923,24 +1931,22 @@ static int tx_init(struct atm_dev *dev)

IF_INIT(printk("Tx MASK REG: 0x%0x\n",
readw(iadev->seg_reg+SEG_MASK_REG));)
- /*---------- Initializing Transmit DLEs ----------*/
- /* allocating 8k memory for transmit DLEs */
- dle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL);
- if (!dle_addr)
- {
- printk(KERN_ERR DEV_LABEL "can't allocate TX DLEs\n");
- return -ENOMEM;
- }
-
- /* find 4k byte boundary within the 8k allocated */
- dle_addr = (u32*)(((u32)dle_addr+(4096-1)) & ~(4096-1));
+
+ /* Allocate 4k (boundary aligned) bytes */
+ dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE,
+ &iadev->tx_dle_dma);
+ if (!dle_addr) {
+ printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+ goto err_out;
+ }
iadev->tx_dle_q.start = (struct dle*)dle_addr;
iadev->tx_dle_q.read = iadev->tx_dle_q.start;
iadev->tx_dle_q.write = iadev->tx_dle_q.start;
iadev->tx_dle_q.end = (struct dle*)((u32)dle_addr+sizeof(struct dle)*DLE_ENTRIES);

/* write the upper 20 bits of the start address to tx list address register */
- writel(virt_to_bus(dle_addr) & 0xfffff000, iadev->dma+IPHASE5575_TX_LIST_ADDR);
+ writel(iadev->tx_dle_dma & 0xfffff000,
+ iadev->dma + IPHASE5575_TX_LIST_ADDR);
writew(0xffff, iadev->seg_reg+SEG_MASK_REG);
writew(0, iadev->seg_reg+MODE_REG_0);
writew(RESET_SEG, iadev->seg_reg+SEG_COMMAND_REG);
@@ -1984,23 +1990,28 @@ static int tx_init(struct atm_dev *dev)
buf_desc_ptr++;
tx_pkt_start += iadev->tx_buf_sz;
}
- iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(caddr_t), GFP_KERNEL);
+ iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(struct cpcs_trailer_desc), GFP_KERNEL);
if (!iadev->tx_buf) {
printk(KERN_ERR DEV_LABEL " couldn't get mem\n");
- return -EAGAIN;
+ goto err_free_dle;
}
for (i= 0; i< iadev->num_tx_desc; i++)
{
+ struct cpcs_trailer *cpcs;

- iadev->tx_buf[i] = kmalloc(sizeof(struct cpcs_trailer),
- GFP_KERNEL|GFP_DMA);
- if(!iadev->tx_buf[i]) {
+ cpcs = kmalloc(sizeof(*cpcs), GFP_KERNEL|GFP_DMA);
+ if(!cpcs) {
printk(KERN_ERR DEV_LABEL " couldn't get freepage\n");
- return -EAGAIN;
+ goto err_free_tx_bufs;
}
+ iadev->tx_buf[i].cpcs = cpcs;
+ iadev->tx_buf[i].dma_addr = pci_map_single(iadev->pci,
+ cpcs, sizeof(*cpcs), PCI_DMA_TODEVICE);
}
iadev->desc_tbl = kmalloc(iadev->num_tx_desc *
sizeof(struct desc_tbl_t), GFP_KERNEL);
+ if(!iadev->desc_tbl)
+ goto err_free_all_tx_bufs;

/* Communication Queues base address */
i = TX_COMP_Q * iadev->memSize;
@@ -2128,7 +2139,7 @@ static int tx_init(struct atm_dev *dev)
iadev->testTable = kmalloc(sizeof(long)*iadev->num_vc, GFP_KERNEL);
if (!iadev->testTable) {
printk("Get freepage failed\n");
- return -EAGAIN;
+ goto err_free_desc_tbl;
}
for(i=0; i<iadev->num_vc; i++)
{
@@ -2137,7 +2148,7 @@ static int tx_init(struct atm_dev *dev)
iadev->testTable[i] = kmalloc(sizeof(struct testTable_t),
GFP_KERNEL);
if (!iadev->testTable[i])
- return -ENOMEM;
+ goto err_free_test_tables;
iadev->testTable[i]->lastTime = 0;
iadev->testTable[i]->fract = 0;
iadev->testTable[i]->vc_status = VC_UBR;
@@ -2193,7 +2204,30 @@ static int tx_init(struct atm_dev *dev)
iadev->tx_pkt_cnt = 0;
iadev->rate_limit = iadev->LineRate / 3;

- return 0;
+ return 0;
+
+err_free_test_tables:
+ while (--i >= 0)
+ kfree(iadev->testTable[i]);
+ kfree(iadev->testTable);
+err_free_desc_tbl:
+ kfree(iadev->desc_tbl);
+err_free_all_tx_bufs:
+ i = iadev->num_tx_desc;
+err_free_tx_bufs:
+ while (--i >= 0) {
+ struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+ pci_unmap_single(iadev->pci, desc->dma_addr,
+ sizeof(*desc->cpcs), PCI_DMA_TODEVICE);
+ kfree(desc->cpcs);
+ }
+ kfree(iadev->tx_buf);
+err_free_dle:
+ pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+ iadev->tx_dle_dma);
+err_out:
+ return -ENOMEM;
}

static void ia_int(int irq, void *dev_id, struct pt_regs *regs)
@@ -2455,6 +2489,33 @@ static unsigned char ia_phy_get(struct a
return readl(INPH_IA_DEV(dev)->phy+addr);
}

+static void ia_free_tx(IADEV *iadev)
+{
+ int i;
+
+ kfree(iadev->desc_tbl);
+ for (i = 0; i < iadev->num_vc; i++)
+ kfree(iadev->testTable[i]);
+ kfree(iadev->testTable);
+ for (i = 0; i < iadev->num_tx_desc; i++) {
+ struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+ pci_unmap_single(iadev->pci, desc->dma_addr,
+ sizeof(*desc->cpcs), PCI_DMA_TODEVICE);
+ kfree(desc->cpcs);
+ }
+ kfree(iadev->tx_buf);
+ pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+ iadev->tx_dle_dma);
+}
+
+static void ia_free_rx(IADEV *iadev)
+{
+ kfree(iadev->rx_open);
+ pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+ iadev->rx_dle_dma);
+}
+
#if LINUX_VERSION_CODE >= 0x20312
static int __init ia_start(struct atm_dev *dev)
#else
@@ -2462,7 +2523,7 @@ __initfunc(static int ia_start(struct at
#endif
{
IADEV *iadev;
- int error = 1;
+ int error;
unsigned char phy;
u32 ctrl_reg;
IF_EVENT(printk(">ia_start\n");)
@@ -2470,7 +2531,8 @@ __initfunc(static int ia_start(struct at
if (request_irq(iadev->irq, &ia_int, SA_SHIRQ, DEV_LABEL, dev)) {
printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
dev->number, iadev->irq);
- return -EAGAIN;
+ error = -EAGAIN;
+ goto err_out;
}
/* @@@ should release IRQ on error */
/* enabling memory + master */
@@ -2480,8 +2542,8 @@ __initfunc(static int ia_start(struct at
{
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
"master (0x%x)\n",dev->number, error);
- free_irq(iadev->irq, dev);
- return -EIO;
+ error = -EIO;
+ goto err_free_irq;
}
udelay(10);

@@ -2515,15 +2577,11 @@ __initfunc(static int ia_start(struct at

ia_hw_type(iadev);
error = tx_init(dev);
- if (error) {
- free_irq(iadev->irq, dev);
- return error;
- }
+ if (error)
+ goto err_free_irq;
error = rx_init(dev);
- if (error) {
- free_irq(iadev->irq, dev);
- return error;
- }
+ if (error)
+ goto err_free_tx;

ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG);
writel(ctrl_reg | CTRL_FE_RST, iadev->reg+IPHASE5575_BUS_CONTROL_REG);
@@ -2538,33 +2596,36 @@ __initfunc(static int ia_start(struct at

if (iadev->phy_type & FE_25MBIT_PHY) {
ia_mb25_init(iadev);
- return 0;
- }
- if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY)) {
+ } else if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY)) {
ia_suni_pm7345_init(iadev);
- return 0;
- }
-
- error = suni_init(dev);
- if (error) {
- free_irq(iadev->irq, dev);
- return error;
- }
-
- /* Enable interrupt on loss of signal SUNI_RSOP_CIE 0x10
- SUNI_RSOP_CIE_LOSE - 0x04
- */
- ia_phy_put(dev, ia_phy_get(dev,0x10) | 0x04, 0x10);
+ } else {
+ error = suni_init(dev);
+ if (error)
+ goto err_free_rx;
+ /*
+ * Enable interrupt on loss of signal
+ * SUNI_RSOP_CIE - 0x10
+ * SUNI_RSOP_CIE_LOSE - 0x04
+ */
+ ia_phy_put(dev, ia_phy_get(dev, 0x10) | 0x04, 0x10);
#ifndef MODULE
- error = dev->phy->start(dev);
- if (error) {
- free_irq(iadev->irq, dev);
- return error;
- }
+ error = dev->phy->start(dev);
+ if (error)
+ goto err_free_rx;
#endif
- /* Get iadev->carrier_detect status */
- IaFrontEndIntr(iadev);
- return 0;
+ /* Get iadev->carrier_detect status */
+ IaFrontEndIntr(iadev);
+ }
+ return 0;
+
+err_free_rx:
+ ia_free_rx(iadev);
+err_free_tx:
+ ia_free_tx(iadev);
+err_free_irq:
+ free_irq(iadev->irq, dev);
+err_out:
+ return error;
}

static void ia_close(struct atm_vcc *vcc)
@@ -2873,10 +2934,10 @@ static int ia_pkt_tx (struct atm_vcc *vc
struct tx_buf_desc *buf_desc_ptr;
int desc;
int comp_code;
- unsigned int addr;
int total_len, pad, last;
struct cpcs_trailer *trailer;
struct ia_vcc *iavcc;
+
iadev = INPH_IA_DEV(vcc->dev);
iavcc = INPH_IA_VCC(vcc);
if (!iavcc->txing) {
@@ -2961,13 +3022,9 @@ static int ia_pkt_tx (struct atm_vcc *vc
IF_TX(printk("ia packet len:%d padding:%d\n", total_len, pad);)

/* Put the packet in a tx buffer */
- if (!iadev->tx_buf[desc-1])
- printk("couldn't get free page\n");
-
+ trailer = iadev->tx_buf[desc-1].cpcs;
IF_TX(printk("Sent: skb = 0x%x skb->data: 0x%x len: %d, desc: %d\n",
(u32)skb, (u32)skb->data, skb->len, desc);)
- addr = virt_to_bus(skb->data);
- trailer = (struct cpcs_trailer*)iadev->tx_buf[desc-1];
trailer->control = 0;
/*big endian*/
trailer->length = ((skb->len & 0xff) << 8) | ((skb->len & 0xff00) >> 8);
@@ -2983,6 +3040,7 @@ static int ia_pkt_tx (struct atm_vcc *vc
buf_desc_ptr = (struct tx_buf_desc *)(iadev->seg_ram+TX_DESC_BASE);
buf_desc_ptr += desc; /* points to the corresponding entry */
buf_desc_ptr->desc_mode = AAL5 | EOM_EN | APP_CRC32 | CMPL_INT;
+ /* Huh ? p.115 of users guide describes this as a read-only register */
writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
buf_desc_ptr->vc_index = vcc->vci;
buf_desc_ptr->bytes = total_len;
@@ -2993,7 +3051,8 @@ static int ia_pkt_tx (struct atm_vcc *vc
/* Build the DLE structure */
wr_ptr = iadev->tx_dle_q.write;
memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr));
- wr_ptr->sys_pkt_addr = addr;
+ wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) |
buf_desc_ptr->buf_start_lo;
/* wr_ptr->bytes = swap(total_len); didn't seem to affect ?? */
@@ -3011,7 +3070,7 @@ static int ia_pkt_tx (struct atm_vcc *vc
wr_ptr = iadev->tx_dle_q.start;

/* Build trailer dle */
- wr_ptr->sys_pkt_addr = virt_to_bus(iadev->tx_buf[desc-1]);
+ wr_ptr->sys_pkt_addr = iadev->tx_buf[desc-1].dma_addr;
wr_ptr->local_pkt_addr = ((buf_desc_ptr->buf_start_hi << 16) |
buf_desc_ptr->buf_start_lo) + total_len - sizeof(struct cpcs_trailer);

@@ -3163,135 +3222,130 @@ static const struct atmdev_ops ops = {
};


-#if LINUX_VERSION_CODE >= 0x20312
-int __init ia_detect(void)
-#else
-__initfunc(int ia_detect(void))
-#endif
+static int __devinit ia_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
struct atm_dev *dev;
IADEV *iadev;
unsigned long flags;
- int index = 0;
- struct pci_dev *prev_dev;
- if (!pci_present()) {
- printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");
- return 0;
- }
+ int ret;
+
iadev = kmalloc(sizeof(*iadev), GFP_KERNEL);
- if (!iadev) return -ENOMEM;
- memset((char*)iadev, 0, sizeof(*iadev));
- prev_dev = NULL;
- while((iadev->pci = pci_find_device(PCI_VENDOR_ID_IPHASE,
- PCI_DEVICE_ID_IPHASE_5575, prev_dev))) {
- IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
- iadev->pci->bus->number, PCI_SLOT(iadev->pci->devfn),
- PCI_FUNC(iadev->pci->devfn));)
- if (pci_enable_device(iadev->pci)) break;
- dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
- if (!dev) break;
- IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n",
- dev->number);)
- INPH_IA_DEV(dev) = iadev;
- // TODO: multi_board using ia_boards logic in cleanup_module
- ia_dev[index] = iadev;
- _ia_dev[index] = dev;
- IF_INIT(printk("dev_id = 0x%x iadev->LineRate = %d \n",
- (u32)dev, iadev->LineRate);)
- iadev_count++;
- spin_lock_init(&iadev->misc_lock);
- spin_lock_irqsave(&iadev->misc_lock, flags);
- if (ia_init(dev) || ia_start(dev)) {
- atm_dev_deregister(dev);
- IF_INIT(printk("IA register failed!\n");)
- ia_dev[index] = NULL;
- _ia_dev[index] = NULL;
- iadev_count--;
- spin_unlock_irqrestore(&iadev->misc_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&iadev->misc_lock, flags);
- IF_EVENT(printk("iadev_count = %d\n", iadev_count);)
- prev_dev = iadev->pci;
- iadev->next_board = ia_boards;
- ia_boards = dev;
- iadev = kmalloc(sizeof(*iadev), GFP_KERNEL);
- if (!iadev) break;
- memset((char*)iadev, 0, sizeof(*iadev));
- index++;
- dev = NULL;
- }
- return index;
-}
-
+ if (!iadev) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ memset(iadev, 0, sizeof(*iadev));
+ iadev->pci = pdev;

-#ifdef MODULE
-
-int init_module(void)
-{
- IF_EVENT(printk(">ia init_module\n");)
- if (!ia_detect()) {
- printk(KERN_ERR DEV_LABEL ": no adapter found\n");
- return -ENXIO;
- }
- ia_timer.expires = jiffies + 3*HZ;
- add_timer(&ia_timer);
-
- return 0;
-}
-
-
-void cleanup_module(void)
-{
- struct atm_dev *dev;
- IADEV *iadev;
- unsigned short command;
- int i, j= 0;
-
- IF_EVENT(printk(">ia cleanup_module\n");)
- if (MOD_IN_USE)
- printk("ia: module in use\n");
- del_timer(&ia_timer);
- while(ia_dev[j])
- {
- dev = ia_boards;
- iadev = INPH_IA_DEV(dev);
- ia_boards = iadev->next_board;
-
- /* disable interrupt of lost signal */
- ia_phy_put(dev, ia_phy_get(dev,0x10) & ~(0x4), 0x10);
- udelay(1);
-
- /* De-register device */
- atm_dev_deregister(dev);
- IF_EVENT(printk("iav deregistered at (itf:%d)\n", dev->number);)
- for (i= 0; i< iadev->num_tx_desc; i++)
- kfree(iadev->tx_buf[i]);
- kfree(iadev->tx_buf);
- /* Disable memory mapping and busmastering */
- if (pci_read_config_word(iadev->pci,
- PCI_COMMAND, &command) != 0)
- {
- printk("ia: can't read PCI_COMMAND.\n");
- }
- command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- if (pci_write_config_word(iadev->pci,
- PCI_COMMAND, command) != 0)
- {
- printk("ia: can't write PCI_COMMAND.\n");
- }
- free_irq(iadev->irq, dev);
- iounmap((void *) iadev->base);
- kfree(iadev);
- j++;
- }
- /* and voila whatever we tried seems to work. I don't know if it will
- fix suni errors though. Really doubt that. */
- for (i = 0; i<8; i++) {
- ia_dev[i] = NULL;
- _ia_dev[i] = NULL;
- }
-}
+ IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));)
+ if (pci_enable_device(pdev)) {
+ ret = -ENODEV;
+ goto err_out_free_iadev;
+ }
+ dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_out_disable_dev;
+ }
+ INPH_IA_DEV(dev) = iadev;
+ IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", dev->number);)
+ IF_INIT(printk("dev_id = 0x%x iadev->LineRate = %d \n", (u32)dev,
+ iadev->LineRate);)
+
+ ia_dev[iadev_count] = iadev;
+ _ia_dev[iadev_count] = dev;
+ iadev_count++;
+ spin_lock_init(&iadev->misc_lock);
+ /* First fixes first. I don't want to think about this now. */
+ spin_lock_irqsave(&iadev->misc_lock, flags);
+ if (ia_init(dev) || ia_start(dev)) {
+ IF_INIT(printk("IA register failed!\n");)
+ iadev_count--;
+ ia_dev[iadev_count] = NULL;
+ _ia_dev[iadev_count] = NULL;
+ spin_unlock_irqrestore(&iadev->misc_lock, flags);
+ ret = -EINVAL;
+ goto err_out_deregister_dev;
+ }
+ spin_unlock_irqrestore(&iadev->misc_lock, flags);
+ IF_EVENT(printk("iadev_count = %d\n", iadev_count);)

-#endif
+ iadev->next_board = ia_boards;
+ ia_boards = dev;
+
+ pci_set_drvdata(pdev, dev);
+
+ return 0;
+
+err_out_deregister_dev:
+ atm_dev_deregister(dev);
+err_out_disable_dev:
+ pci_disable_device(pdev);
+err_out_free_iadev:
+ kfree(iadev);
+err_out:
+ return ret;
+}
+
+static void __devexit ia_remove_one(struct pci_dev *pdev)
+{
+ struct atm_dev *dev = pci_get_drvdata(pdev);
+ IADEV *iadev = INPH_IA_DEV(dev);
+
+ ia_phy_put(dev, ia_phy_get(dev,0x10) & ~(0x4), 0x10);
+ udelay(1);
+
+ /* De-register device */
+ free_irq(iadev->irq, dev);
+ iadev_count--;
+ ia_dev[iadev_count] = NULL;
+ _ia_dev[iadev_count] = NULL;
+ atm_dev_deregister(dev);
+ IF_EVENT(printk("iav deregistered at (itf:%d)\n", dev->number);)
+
+ iounmap((void *) iadev->base);
+ pci_disable_device(pdev);
+
+ ia_free_rx(iadev);
+ ia_free_tx(iadev);
+
+ kfree(iadev);
+}
+
+static struct pci_device_id ia_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_IPHASE, 0x0008, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_IPHASE, 0x0009, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, ia_pci_tbl);
+
+static struct pci_driver ia_driver = {
+ .name = DEV_LABEL,
+ .id_table = ia_pci_tbl,
+ .probe = ia_init_one,
+ .remove = ia_remove_one,
+};
+
+static int __init ia_init_module(void)
+{
+ int ret;
+
+ ret = pci_module_init(&ia_driver);
+ if (ret >= 0) {
+ ia_timer.expires = jiffies + 3*HZ;
+ add_timer(&ia_timer);
+ }
+ return ret;
+}
+
+static void __exit ia_cleanup_module(void)
+{
+ pci_unregister_driver(&ia_driver);
+
+ del_timer(&ia_timer);
+}

+module_init(ia_init_module);
+module_exit(ia_cleanup_module);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/