/* ia_flip.c */ /* Copyright (c) 2000 James M. Westall, Dept of Computer Science, * Clemson University, Clemson SC 29634 USA * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * To obtain a copy of the GNU General Public License write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * This software is derived from software developed by the Interphase * corporation and released by them in September 2000. The software * released by Interphase carried the following copyright notice: * * * Copyright (C) 1993 * Interphase Corporation, Dallas, TX 75234 * All Rights Reserved * * This code contains confidential information and trade secrets of * Interphase Corporation which shall not be reproduced or transferred * to other programs or disclosed to others or used for manufacturing * or any other purpose without prior written permission of Interphase * Corporation. Use of copyright notice is precautionary and does not * imply publication or intent thereof. */ /* * FILE: ia_flip.c * * DESCRIPTION: * This file contains all the device driver entry points * the main interrupt handler * * FUNCTIONS: * EXPORTED: * see below. * * STATIC: * fsee below. * * GLOBAL DATA: * */ #include "ia_defs.h" #define MEM_VALID 0xfffffff0 #define IA5515_MAJOR 17 MODULE_AUTHOR("Mike Westall "); MODULE_PARM(interface, "1i"); static int interface = 0; extern struct timeval xtime; ia_softc_t *softc; /* * Configuration */ uint_t ia_debug = 0; /* If set -> Print info useful for debug */ uint_t ia_xoff_disable = 0; /* If set -> Do not enable link XON/XOFF */ uint_t ia_revb = 1; /* If set -> Initialize for Rev-B hardware */ uint_t ia_stm = 0; /* If set -> STM-1 mode (Europe's SONET) */ uint_t ia_rx_putq = 0; /* If set -> Do a putq from intr. on RX */ uint_t ia_tx_vpi = 0; /* VPI for transmission */ uint_t ia_aal5 = 1; /* A character device interface for initialization */ /* and management operations is provided here. */ static struct file_operations ia5515_fops = { #if LINUX_VERSION_CODE >= 0x20312 NULL, /* module owner */ #endif NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* select */ ia_ioctl, /* ioctl */ NULL, /* mmap */ ia_open, /* open */ NULL, /* flush */ ia_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ }; static void ia_rx_free (void *rx_bufp) { } /*F * FUNCTION: ia_intr * * DESCRIPTION: * This is the main interrupt routine. Read the SMUX interrupt * status register and determine which of the 3 sources are * interrupting. Call other routines to do the real work. * * ARGUMENTS: * arg Pointer to the device statue structure * * ENVIRONMENT: * Called when an interrupt is received from the controller * * RETURN: * DDI_INTR_CLAIMED if out controller interrupting * DDI_INTR_UNCLAIMED if our controller not interrupting * * NOTES: * SMUX interrupt 0 - Addgen interrupt * SMUX interrupt 1 - F-FRED/Front-End interrupt * SMUX interrupt 2 - R-FRED interrupt * */ void ia_intr( int irq, void *arg) { ia_softc_t *softc = (ia_softc_t *)arg; ffred_t *ffred = softc->brd_regs.ffred; rfred_t *rfred = softc->brd_regs.rfred; ia_suni_t *suni = softc->brd_regs.suni; uint_t flip_intr_status; uint_t ffred_intr_status; uint_t rfred_intr_status; unsigned int lockflags; /* * Check to see if our controller in interrupting. */ flip_intr_status = ia_get32(&softc->brd_regs.flipper->fl_status); flip_intr_status &= ~FL_STAT_RESERVED; #ifdef DBG_INTR printk("IA 5515: I stat is %x \n", flip_intr_status); #endif /* * If this is not our interrupt... return now. */ if (flip_intr_status == 0) { return; } softc->driver_stat.interrupts++; /* * We run this stuff in a loop so that if another interrupt becomes * pending while we handle this one, we can go ahead and process it */ while (flip_intr_status != 0) { /* * R-FRED interrupt? */ if (flip_intr_status & FL_STAT_RFREDINT) { #ifdef DBG_INTR printk("IA 5515: RFred int stat is %x \n", rfred_intr_status); #endif spin_lock_irqsave(&softc->recvlock, lockflags); softc->driver_stat.rfred_intr++; ia_rfred_handler(softc); spin_unlock_irqrestore(&softc->recvlock, lockflags); } /* * F-FRED interrupt? */ if (flip_intr_status & FL_STAT_FFREDINT) { uint_t ffred_intr_status; softc->driver_stat.tfred_intr++; ffred_intr_status = ia_getw(&ffred->intr_status_reg); ffred_intr_status &= 0xffff; #ifdef DBG_INTR printk("IA 5515: FFred int stat is %x \n", ffred_intr_status); #endif ia_tx_tcq_intr(softc); /* * Has TCQ changed state from empty to non empty? */ if (ffred_intr_status & F_TCQ_NOT_EMPTY) { /* printk("IA 5515: FFred TCQ interrupt \n"); */ } else { #ifdef DBG_INTR printk("IA 5515: unknown F-FRED intr \n"); #endif } } /* * Front-End interrupt? */ if (flip_intr_status & FL_STAT_FEINT) { printk("SUNI stat was %x \n", ia_get32(&suni->suni_master_intr_stat) & 0x7f); if (ia_get32 (&suni->suni_master_intr_stat) & 0x7f) { softc->driver_stat.suni_intr++; /* ia_suni_intr (softc); */ } else { printk("IA 5515: unknown Front-end intr"); } } /* * Receive DLE interrupt? */ if (flip_intr_status & FL_STAT_DLERINT) { spin_lock_irqsave(&softc->recvlock, lockflags); softc->driver_stat.addgen_intr++; ia_put32 (&softc->brd_regs.flipper->fl_status, FL_STAT_DLERINT); #ifdef DBG_INTR printk("IA 5515: DLE Receive interrupt \n"); #endif ia_host_rx_intr(softc); spin_unlock_irqrestore(&softc->recvlock, lockflags); } /* * Transmit DLE interrupt? */ if (flip_intr_status & FL_STAT_DLETINT) { ia_put32(&softc->brd_regs.flipper->fl_status, FL_STAT_DLETINT); #ifdef DBG_INTR printk("IA 5515: DLE Tx interrupt \n"); #endif } if (flip_intr_status & FL_STAT_ERRINT) { printk("IA 5515: FATAL PCI Error cmd/stat is is %x \n", softc->brd_regs.config->fl_cfg_cmd_status); ia_card_reset(softc); break; ia_put32(&softc->brd_regs.flipper->fl_status, FL_STAT_ERRINT); if (ia_debug) printk("IA 5515: ia_intr: unknown error intr \n"); } flip_intr_status = softc->brd_regs.flipper->fl_status; flip_intr_status &= ~FL_STAT_RESERVED; } while (flip_intr_status); return; } /* Give back allocated resources and shut the board down */ ia_close( ia_softc_t *softc) { ia_dma_t *dma; dma = &softc->tx_dma; if (dma->taddr) kfree(dma->taddr); dma = &softc->rx_dma; if (dma->taddr) kfree(dma->taddr); dma = &softc->tx_list_dma; if (dma->taddr) kfree(dma->taddr); dma = &softc->rx_list_dma; if (dma->taddr) kfree(dma->taddr); unregister_chrdev(IA5515_MAJOR, "ia5515"); free_irq(softc->irq, softc); iounmap(softc->pci_base_v); kfree(softc); } /**/ /* Init VC structures used in managing standalone */ /* driver PVCs.. */ ia_init_pvcs( ia_softc_t *softc) { int i; ia_pvc_desc_t *pvc; pvc = softc->pvctab + 1; for (i = 1; i < NUM_PVCS; i++) { #if LINUX_VERSION_CODE >= 0x20303 init_waitqueue_head(&pvc->input_wait); #else pvc->input_wait = NULL; #endif pvc->rdybufhead = NULL; pvc->rdybuftail = NULL; pvc += 1; } } /*F * FUNCTION: ia_alloc_buffers * * DESCRIPTION: * Allocate and dma map transmit and receive data buffers. * * ARGUMENTS: * softc device state structure * * ENVIRONMENT: * Only called from ia_attach * * RETURN: * IA_SUCCESS if able to allocate all the resources * IA_FAIL on failure * * NOTES: * */ /**/ /* Current strategy relies upon permanently allocated kernel buffers */ /* through which all packets pass.. Yes, it does cost an extra copy. */ /* But its simple, reliable, and was used in the Solaris driver from */ /* which this stuff was derived. One day (maybe) we'll fix it. */ int ia_alloc_buffers ( ia_softc_t *softc) { caddr_t dmac_address; int dmac_size; ia_dma_t *dma; int err; size_t total_len; int offset; int i, j; int tx_buf_size; int rx_buf_size; /* TX buffers are used only for building CS trailer */ /* RX buffers are not presently used at all. */ tx_buf_size = 16; rx_buf_size = 16; /* *------------------------ * Set up transmit buffers *------------------------ */ dma = &softc->tx_dma; dma->len = tx_buf_size * softc->num_tx_bufs + 64; #ifdef DBG_INIT printk("IA 5515: Preparing to alloc %d Tx bufs of len %d \n", softc->num_tx_bufs, tx_buf_size); #endif dma->addr = kmalloc(dma->len, GFP_DMA); dma->mem_alloc_done = 1; dma->taddr = dma->addr; offset = ROUND_UP(dma->addr, 64) - (int)dma->addr; dma->addr += offset; dma->real_len = dma->len - offset; dmac_size = dma->real_len; dmac_address = (caddr_t)virt_to_bus(dma->addr); #ifdef DBG_INIT printk("IA 5515: TX pool at virt %x phys %x \n", dma->addr, dmac_address); #endif for (i = 0; i < softc->num_tx_bufs; i++) { softc->tx_buf[i].addr = dma->addr + i * tx_buf_size; softc->tx_buf[i].offset = i * tx_buf_size; total_len = tx_buf_size; for (j = 0; j < MAX_SGE; j++) { softc->tx_buf[i].daddr[j] = dmac_address; #ifdef DBG_INIT printk("IA 5515: Tx %3d %3d %6d %6d \n", i, j, dmac_address, dmac_size); #endif if (dmac_size >= total_len) { softc->tx_buf[i].dlen[j] = total_len; dmac_address += total_len; dmac_size -= total_len; break; } } if (j == MAX_SGE) { printk("IA 5515: Tx DMA buffer mapping failure \n"); return IA_FAIL; } } /* *----------------------- * Set up receive buffers *----------------------- */ dma = &softc->rx_dma; dma->len = rx_buf_size * NUM_RX_BUFS + 64; #ifdef DBG_INIT printk("IA 5515: Preparing to alloc %d Rx bufs of len %d \n", NUM_RX_BUFS, rx_buf_size); #endif dma->addr = kmalloc(dma->len, GFP_DMA); dma->taddr = dma->addr; dma->mem_alloc_done = 1; offset = ROUND_UP(dma->addr, 64) - (int)dma->addr; dma->addr += offset; dma->real_len = dma->len - offset; dmac_size = dma->real_len; dmac_address = (caddr_t)virt_to_bus(dma->addr); #ifdef DBG_INIT printk("IA 5515: Rx pool at virt %x phys %x \n", dma->addr, dmac_address); #endif for (i = 0; i < NUM_RX_BUFS; i++) { softc->rx_buf[i].addr = dma->addr + i * rx_buf_size; softc->rx_buf[i].offset = i * rx_buf_size; softc->rx_buf[i].softc = softc; softc->rx_buf[i].index = i; softc->rx_buf[i].next = &softc->rx_buf[i+1]; total_len = rx_buf_size; for (j = 0; j < MAX_SGE; j++) { softc->rx_buf[i].daddr[j] = dmac_address; #ifdef DBG_INIT printk("IA 5515: Rx %3d %3d %6d %6d \n", i, j, dmac_address, dmac_size); #endif if (dmac_size >= total_len) { softc->rx_buf[i].dlen[j] = total_len; dmac_address += total_len; dmac_size -= total_len; break; } else { j = MAX_SGE; break; } } if (j == MAX_SGE) { printk("IA 5515: Rx DMA buffer mapping failure \n"); return IA_FAIL; } } softc->rx_buf[i-1].next = NULL; softc->rx_buf_free = &softc->rx_buf[0]; softc->rx_buf_freetail = &softc->rx_buf[i-1]; softc->rx_buf_dmahead = NULL; softc->rx_buf_dmatail = NULL; return IA_SUCCESS; } /**/ /* Initialization driver function */ ia_init(ia_softc_t *softc) { caddr_t vaddr; unsigned short command; unsigned short status; int error; int i; int val; ia_card_type(softc); #if LINUX_VERSION_CODE >= 0x20312 softc->real_base = softc->ia_dev->resource[0].start; #else softc->real_base = softc->ia_dev->base_address[0]; #endif softc->irq = softc->ia_dev->irq; printk("IA 5515: Memory base is %x \n", softc->real_base); printk("IA 5515: IRQ is %x \n", softc->irq); if (pci_write_config_dword(softc->ia_dev, PCI_BASE_ADDRESS_0, 0xffffffff) != PCIBIOS_SUCCESSFUL) { printk("IA 5515: Base address write failed \n"); return -EINVAL; } if(pci_read_config_dword(softc->ia_dev, PCI_BASE_ADDRESS_0, &(softc->pci_map_size)) != PCIBIOS_SUCCESSFUL) { printk("IA 5515: Base address read failed \n"); return -EINVAL; } softc->pci_map_size &= PCI_BASE_ADDRESS_MEM_MASK; softc->pci_map_size = ~softc->pci_map_size + 1; printk("IA 5515: Memory size is %x \n", softc->pci_map_size); /* Restore correct base address */ if(pci_write_config_dword(softc->ia_dev, PCI_BASE_ADDRESS_0, softc->real_base) != PCIBIOS_SUCCESSFUL) { printk("IA 5515: Base address rewrite failed \n"); return -EINVAL; } /* strip flags (last 4 bits ) ---> mask with 0xfffffff0 */ softc->real_base &= MEM_VALID; /* Enable the board */ error = pci_read_config_word(softc->ia_dev, PCI_COMMAND, &command); command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); if ((error = pci_write_config_word(softc->ia_dev, PCI_COMMAND, command))) { printk("IA 5515: Activate board failed \n"); return error; } /* * Delay before doing any mem accesses */ udelay(1000); #ifdef DBG_INIT error = pci_read_config_word(softc->ia_dev, PCI_STATUS, &status); printk("IA 5515: ia_init: PCI STATUS is %x \n", status); pci_read_config_word(softc->ia_dev, PCI_COMMAND, &command); printk("IA 5515: PCI command word is now %x\n", command); printk("IA 5515: Remapping from %x for %x \n", softc->real_base, softc->pci_map_size); #endif /* map the physical address to a virtual address */ vaddr = softc->pci_base_v = (caddr_t)ioremap((unsigned long)softc->real_base, softc->pci_map_size); if (!vaddr) { printk("IA 5515: ioremap failed... goodbye \n"); return(-1); } #ifdef DBG_INIT printk("IA 5515: Board mapped at virtual %x \n", vaddr); #endif softc->ffred_map.f_num_vc = F_NUM_VC_128K; softc->brd_regs.ffred = (ffred_t *)(vaddr + FFRED_OFFSET); softc->brd_regs.rfred = (rfred_t *)(vaddr + RFRED_OFFSET); softc->brd_regs.ctrl = (uint_t *)(vaddr + FECR_OFFSET); softc->brd_regs.suni = (ia_suni_t *)(vaddr + SUNI_OFFSET); softc->brd_regs.flipper = (ia_flip_t *)(vaddr + FLIP_OFFSET); softc->brd_regs.config = (fl_config_t *)(vaddr + CONFIG_OFFSET); softc->brd_regs.tx_tc = (uint_t *)(vaddr + TX_TC_OFFSET); softc->brd_regs.rx_tc = (uint_t *)(vaddr + RX_TC_OFFSET); softc->brd_regs.ffred_mem = (caddr_t)(vaddr + FFRED_MEM_OFFSET); softc->brd_regs.rfred_mem = (caddr_t)(vaddr + RFRED_MEM_OFFSET); memset(softc->brd_regs.ffred_mem, 0, 1024); softc->brd_regs_ph.buf_mem = 0; softc->brd_regs_ph.ffred_mem = 0; softc->brd_regs_ph.rfred_mem = 0; /* * Find the hardware type (FE type and buffer memory size) */ ia_hw_type(softc); /* * Allocate memory for board buffers */ error = ia_alloc_buffers(softc); if (error == IA_FAIL) { printk("IA 5515: Exiting due to buffer init error \n"); return(-1); } /* * Initialize Flipper and DMA */ #ifdef DBG_INIT printk("IA 5515: Calling flip_init \n"); #endif error = ia_flip_init(softc); if (error == IA_FAIL) { printk("IA 5515: Exiting due to flipper init error \n"); return(-1); } #ifdef DBG_INIT printk("IA 5515: Calling fred_init \n"); #endif error = ia_ffred_init(softc); if (error == IA_FAIL) { printk("IA 5515: Exiting due to ffredr init error \n"); return(-1); } error = ia_rfred_init(softc); if (error == IA_FAIL) { printk("IA 5515: Exiting due to rfredr init error \n"); return(-1); } /* * Clear front end reset. */ ia_or32(softc->brd_regs.ctrl, FECR_RESET); udelay((clock_t)1000); /* * Initialize SUNI */ ia_suni_init (softc); /* * Get/Set MAC address */ val = ia_get32(&softc->brd_regs.flipper->fl_mac1); softc->mac_addr[0] = (val ) & 0xff; softc->mac_addr[1] = (val >> 8) & 0xff; softc->mac_addr[2] = (val >> 16) & 0xff; softc->mac_addr[3] = (val >> 24) & 0xff; val = ia_get32(&softc->brd_regs.flipper->fl_mac2); softc->mac_addr[4] = (val ) & 0xff; softc->mac_addr[5] = (val >> 8) & 0xff; printk("IA 5515: MAC address "); for (i=0; i<6; i++) printk("%02x:",softc->mac_addr[i]); printk("\n"); return(0); } char msg[] = "Hello 5515"; /**/ /* Main entry point for module initialization */ int init_module(void) { unsigned char bus = 0; unsigned char devfn = 0; struct pci_dev *pci_dev; struct pci_dev *ia_dev; int found = 0; int rc; aal_parms_t aal; int i; u32 stat; printk("IA 5515: Alloc buffers is at %x \n", ia_alloc_buffers); /* See if we can find a board */ printk("IA 5515: Module initialization beginning \n"); if (!pci_present()) { printk("IA 5515: No PCI Bios!\n"); return(-1); } printk("IA 5515: Bios search for device \n"); pci_dev = NULL; while (pci_dev = pci_find_device(PCI_VENDOR_ID_IPHASE, PCI_DEVICE_ID_IPHASE_5515, pci_dev)) { ia_dev = pci_dev; found += 1; } /* For now.. don't want to worry about multiple board support */ if (found != 1) { printk("IA 5515: Found %d adapters \n" , found); printk("IA 5515: Exactly ONE adapters is required \n"); return(-1); } bus = ia_dev->bus->number; devfn = ia_dev->devfn; printk("IA 5515: Found 1 adapters at bus %d devfn %d \n", ia_dev->bus->number, ia_dev->devfn); /* Allocate the device management data structure ... this */ /* thing is referred to as "softc" throughout */ softc = kmalloc(sizeof(ia_softc_t), GFP_KERNEL); if (softc == 0) { printk("IA 5515: softc memory allocation failed... goodbye \n"); return(-1); } memset(softc, 0, sizeof(ia_softc_t)); softc->ia_dev = ia_dev; #ifdef DBG_INIT printk("IA 5515: softc at %x \n", softc); #endif /* Attempt to initialize the device */ rc = ia_init(softc); if (rc) { printk("IA5515: Device initialization failed.. goodbye \n"); return(-1); } rc = request_irq(ia_dev->irq, (void *)ia_intr, SA_INTERRUPT | SA_SHIRQ, "ia5515", softc); if (rc) { printk("IA 5515: Can't allocate IRQ %d. Code was %d \n", ia_dev->irq, rc); return -EIO; } udelay(100); #ifdef DBG_INIT printk("IA 5515: PCI CMD/STAT is %x \n", softc->brd_regs.config->fl_cfg_cmd_status); printk("IA 5515: Flipper control reg is %x \n", softc->brd_regs.flipper->fl_ctrl); printk("IA 5515: Flipper status reg is %x \n", softc->brd_regs.flipper->fl_status); printk("IA 5515: FFred interrupt reg is %x \n", ia_getw(&softc->brd_regs.ffred->intr_status_reg)); printk("IA 5515: FFred control reg is %x \n", ia_getw(&softc->brd_regs.ffred->cmd_reg)); printk("IA 5515: FFred state reg is %x \n", ia_getw(&softc->brd_regs.ffred->state_reg)); printk("IA 5515: FFred prq rd ptr is %x \n", ia_getw(&softc->brd_regs.ffred->prq_rd_ptr)); printk("IA 5515: FFred prq wt ptr is %x \n", ia_getw(&softc->brd_regs.ffred->prq_wr_ptr)); printk("IA 5515: FFred prq st ptr is %x \n", ia_getw(&softc->brd_regs.ffred->prq_st_adr)); printk("IA 5515: FFred prq ed ptr is %x \n", ia_getw(&softc->brd_regs.ffred->prq_ed_adr)); printk("IA 5515: FFred tcq rd ptr is %x \n", ia_getw(&softc->brd_regs.ffred->tcq_rd_ptr)); printk("IA 5515: FFred tcq wt ptr is %x \n", ia_getw(&softc->brd_regs.ffred->tcq_wr_ptr)); printk("IA 5515: FFred tcq st ptr is %x \n", ia_getw(&softc->brd_regs.ffred->tcq_st_adr)); printk("IA 5515: FFred tcq ed ptr is %x \n", ia_getw(&softc->brd_regs.ffred->tcq_ed_adr)); #endif /* Initialize locks */ spin_lock_init(&softc->xmitlock); spin_lock_init(&softc->recvlock); spin_lock_init(&softc->xramlock); /* Register ourselves with the Linux ATM stack... or export an ioctl() */ /* character device interface */ if (rc = ia5515_register(softc, interface)) { printk( "IA_5515: cannot register atm device %d\n", rc); return(-1); } if (rc = register_chrdev(IA5515_MAJOR, "ia5515", &ia5515_fops)) { printk( "IA_5515: cannot register device /dev/ia5515: received %d\n", rc); return(-1); } softc->last_tx_vcc = 32; for (i = 0; i < NUM_VCCS; i++) skb_queue_head_init(&softc->vcctab[i].tx_backlog); softc->tod = &xtime; do_gettimeofday(&softc->tv); printk("IA_5515: Intialized at %d.%d \n", softc->tv.tv_sec, softc->tv.tv_usec); /* ia_close(softc); */ return(0); } int cleanup_module(void) { ia5515_deregister(softc); ia_card_reset (softc); udelay(500); ia_close(softc); }