/* atmbase.c */ /* This is the base module of the IBM ATM 25Mb */ /* installable device driver... It contains */ /* initialization, cleanup, and the first */ /* level interrupt drivers. */ /* Copyright (c) 1996 Robert Geist and James Westall * Clemson University Dept. of Computer Science * * 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. * * 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. */ #include "atmdd.h" #include "picocode.h" static int int_count = 0; /* An interrupt counter */ static int lcnum = 0; MODULE_AUTHOR("Mike Westall "); MODULE_PARM(interface, "1i"); static int interface = 0; #ifdef CAPTURE_PMD struct pmdtype pmd; #endif /* A character device interface for initialization */ /* and management operations is provided here. */ static struct file_operations atm_fops = { NULL, /* struct module *own */ NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* select */ atm_ioctl, /* ioctl */ NULL, /* mmap */ atm_open, /* open */ NULL, /* flush */ atm_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ }; /* The physical device descriptor structure that is the */ /* root of all data structures. */ struct pddtype ape25; struct pmdtype pmd; static char nvbuf[127]; /**/ /* Read an 8 bit value from the nvram */ int atm_rdnvram( struct pddtype *ape, unsigned int offset) { int val; int count; count = 0; if (offset > 127) return(-1); /* Build the read command word */ val = 1 << 26; /* 1 here means read */ val |= offset << 8; /* index of byte desired */ /* Issue the read command */ /* This reg unlike the others is a 32 bit model */ outl(val, ape->pci_iobase + NVRAM_CNTL); /* Poll until bit 24 indicates that data is ready */ val = inl(ape->pci_iobase + NVRAM_CNTL); while ((val & (1 << 24)) == 0) { count += 1; if (count > 16000) break; val = inl(ape->pci_iobase + NVRAM_CNTL); } /* printk("Nvram value is %8x and count is %d \n", val, count); */ return(val); } /**/ /* Write an 8 bit value into the NVRAM.. Writing */ /* to locations below byte 64 requires a special */ /* code... Use of that code is not implemented */ int atm_wtnvram( struct pddtype *ape, unsigned int offset, unsigned int datum) { int val; int count; /* Validity check the offset */ if (offset < 64) return(-1); if (offset > 127) return(-1); /* Build the read command word */ val = 0 << 26; /* 0 here means write */ val |= offset << 8; /* index of byte desired */ val |= (datum & 0xff); /* Byte to write */ /* Issue the read command */ /* This reg unlike the others is a 32 bit model */ outl(val, ape->pci_iobase + NVRAM_CNTL); printk("Written value is %8x \n", val); /* Poll until bit 24 indicates that write is done */ count = 0; val = inl(ape->pci_iobase + NVRAM_CNTL); while ((val & (1 << 24)) == 0) { count += 1; if (count > 64000) /* Paranoia test */ break; val = inl(ape->pci_iobase + NVRAM_CNTL); } /* printk("Readback value is %8x and count is %d \n", val, count); */ return(val); } /**/ /* Write a 16 bit value to the specified SAP (System */ /* access port) register. */ void atm_wtsreg( struct pddtype *ape, unsigned int regid, unsigned short val) { outw(val, ape->pci_iobase + regid); } /**/ /* Read a 16 bit value from the specified SAP (System */ /* access port) register. */ int atm_rdsreg( struct pddtype *ape, unsigned int regid) { int val; val = inw(ape->pci_iobase + regid); return(val); } /**/ /* Write into the internal address space using the SAP */ /* (System Access Port) address registers. */ void atm_wtvsap( struct pddtype *ape, unsigned int regid, unsigned short val) { unsigned long flags; save_flags(flags); cli(); /* printk("WTVSAP a1 = %x a2 = %x val = %x \n", regid & 0xffff, (regid >> 16) & 0xf0f, val); */ outw( regid & 0xffff, ape->pci_iobase + SAP_ADDR_REG); outw((regid >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); outw( val, ape->pci_iobase + SAP_DATA_PORT); restore_flags(flags); } /**/ /* Read the internal address space using the SAP */ /* (System Access Port) address registers. */ int atm_rdvsap( struct pddtype *ape, unsigned int regid) { int val; unsigned long flags; save_flags(flags); cli(); /* printk("RDVSAP a1 = %x a2 = %x \n", regid & 0xffff, (regid >> 16) & 0xf0f); */ outw( regid & 0xffff, ape->pci_iobase + SAP_ADDR_REG); outw((regid >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); val = inw(ape->pci_iobase + SAP_DATA_PORT); /* printk("RDVSAP val is %x \n", val); */ restore_flags(flags); return(val); } /**/ /* IPL the receive and xmit pico processors */ int atm_iplpp( struct pddtype *ape) { int i; /* Set the base address of the pico ram recv code in the SAP regs */ outw( 0, ape->pci_iobase + SAP_ADDR_REG); outw((RECV_PICO_RAM >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); /* Load the pico code using the auto increment mode */ for (i = 0; i < aal5_recvcnt; i++) { outw(aal5_recvdata[i], ape->pci_iobase + SAP_DATA_PORT_INC); } /* Set the base address of the pico ram xmit code in the SAP regs */ outw( 0, ape->pci_iobase + SAP_ADDR_REG); outw((XMIT_PICO_RAM >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); /* Load the pico code using the auto increment mode */ for (i = 0; i < aal5_xmitcnt; i++) { outw(aal5_xmitdata[i], ape->pci_iobase + SAP_DATA_PORT_INC); } } /**/ /* Increment the 64 bit performance measurement counters */ /* based upon the 16 bit values read from the board. */ void atm_setpmd( unsigned short new, unsigned short *old) { int i; if (*old <= new) { *old = new; return; } /* printk("*old = %x new = %x \n", *old, new); */ *old = new; for (i = 0; i < 3; i++) { old += 1; *old += 1; if (*old != 0) return; } } #ifdef CAPTURE_PMD /**/ /* Increment a peformance measuring event counter */ void atm_incpmd( unsigned int *word) { *word += 1; if (*word == 0) { word += 1; *word += 1; } } /**/ /* Read peformance montoring registers... Triggered by */ /* the 10 ms clock ticker. */ void atm_rdpmregs( struct pddtype *ape) { unsigned short rclp0; unsigned short rclp1; unsigned short roam; unsigned short misrt; unsigned short tclp0; unsigned short tclp1; unsigned short toam; outw(TCLP0_RCELL, ape->pci_iobase + SAP_ADDR_REG); outw((APE25_CONTROL >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); rclp0 = inw(ape->pci_iobase + SAP_DATA_PORT_INC); rclp1 = inw(ape->pci_iobase + SAP_DATA_PORT); outw(MISROUTE_CELL, ape->pci_iobase + SAP_ADDR_REG); outw((APE25_CONTROL >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); misrt = inw(ape->pci_iobase + SAP_DATA_PORT_INC); roam = inw(ape->pci_iobase + SAP_DATA_PORT); outw(TCLP0_TCELL, ape->pci_iobase + SAP_ADDR_REG); outw((APE25_CONTROL >> 16) & 0xf0f, ape->pci_iobase + SAP_EXT_ADDR_REG); tclp0 = inw(ape->pci_iobase + SAP_DATA_PORT_INC); tclp1 = inw(ape->pci_iobase + SAP_DATA_PORT_INC); toam = inw(ape->pci_iobase + SAP_DATA_PORT); atm_setpmd(tclp0, (unsigned short *)pmd.tclp0); atm_setpmd(tclp1, (unsigned short *)pmd.tclp1); atm_setpmd(rclp0, (unsigned short *)pmd.rclp0); atm_setpmd(rclp1, (unsigned short *)pmd.rclp1); #if 0 printk("PMD: %x %x %x %x \n", pmd.tclp0[0], pmd.tclp1[0], pmd.rclp0[0], pmd.rclp1[0]); atm_setpmd(toam, (unsigned short *)pmd.toam); atm_setpmd(roam, (unsigned short *)pmd.roam); atm_setpmd(misrt, (unsigned short *)pmd.misrt); #endif /* printk("Tclp 0 = %8x Rclp0 %8x \n", pmd.tclp0[0], pmd.rclp0[0]); */ } #endif /**/ /* Initialize some miscellaneous registers */ void atm_initregs( struct pddtype *ape) { WT_CNTLREG(ape, IDLE_CELL_HDR_HI, 0); WT_CNTLREG(ape, IDLE_CELL_HDR_LO, 1); WT_CNTLREG(ape, ERROR_REG, 0xffff); WT_CNTLREG(ape, ERROR_REG_MASK, 0xaeff); /* 0xaeff */ WT_CNTLREG(ape, OAM_MASK, 0xecea); WT_CNTLREG(ape, OAM_B1_MASK1, 0xff40); WT_CNTLREG(ape, OAM_B1_MASK2, 0xff41); WT_CNTLREG(ape, PCI_ERROR_MASK, 0xFFFF); WT_CNTLREG(ape, CERR_CNT, 0xffff); WT_CNTLREG(ape, SERR_CNT, 0xffff); WT_CNTLREG(ape, FR_TIMEOUT_VAL, 0x0032); /* WT_CNTLREG(ape, SAP_NVRAM_REG, 0x00c8); */ } /**/ /* APE 25 Error interrupt handler */ void atm_error( struct pddtype *ape, unsigned short sisr) { unsigned short error; unsigned short mask; int i; int regid; error = RD_CNTLREG(ape, ERROR_REG); if (error & 0x2000) { printk("IBM APE25: New signal validity is %d \n", (error >> 12) & 1); } else if (error & 0x0800) { printk("IBM APE25: CErr Error \n"); WT_CNTLREG(ape, CERR_CNT, 0xffff); } else if (error & 0x0400) { printk("IBM APE25: SErr Error \n"); WT_CNTLREG(ape, SERR_CNT, 0xffff); } else if (error & 0x0200) { printk("IBM APE25: New Nlock validity is %d \n", (error >> 8) & 1); } #ifdef CAPTURE_PMD mask = 0x01; regid = T0MQ_MIS_CNT; for (i = 0; i < 8; i++) { if (error & mask) { /* printk("IBM APE25: TMQ %d missed service \n", i); */ pmd.tmqmisses[i] += 1; WT_CNTLREG(ape, regid, 0xff); } regid += 2; mask <<= 1; } #endif } #define SKB_ADD_THRESHOLD 30 /* 300 msec. */ void atm_tick( struct pddtype *ape, unsigned short sisr) { unsigned short mask; int i; static unsigned long tocks = 0; static unsigned long lastadd = 0; tocks += 1; #ifdef CAPTURE_PMD atm_rdpmregs(ape); atm_incpmd(pmd.intcount); mask = 0x8000; for (i = 0; i < 16; i++) { if (sisr & mask) atm_incpmd(pmd.intcats[i]); mask >>= 1; } #endif } /**/ /* APE 25 Interrupt service routine */ void ape25_irq( int irq, /* IRQ number (assigned by PCI) */ void *devid, /* -> device structure */ struct pt_regs *regs) { unsigned short savemisr; unsigned short sisr; unsigned short mask; unsigned long flags; int i; struct pddtype *ape; ape = (struct pddtype *)devid; /* Reading the SISR is said to clear the interrupt... A recursive */ /* call to the handler could be fatal.. so we prevent it by */ /* zeroing the MISR. */ savemisr = atm_rdsreg(ape, MISR); atm_wtsreg(ape, MISR, 0); sisr = atm_rdsreg(ape, SISR); /* Maybe not our interrupt */ if ((sisr & savemisr) == 0) { atm_wtsreg(ape, MISR, savemisr); /* Restore misr */ return; } #ifdef DEBUG_BASE if (sisr != 1) /* sisr = 1 => 10 ms tick */ printk("IBM APE25: sisr = %04x \n", sisr); if (sisr & 0x80) /* No free frame buffers */ { printk("IBM APE25: sisr = %04x \n", sisr); } if (sisr & 0x40) /* No free cell buffers */ { printk("IBM APE25: sisr = %04x \n", sisr); } #endif int_count += 1; /* Handle receive complete interrupts */ if (sisr & 0xff00) { spin_lock_irqsave (&ape->recvlock, flags); atm_recvint(ape, sisr); spin_unlock_irqrestore(&ape->recvlock, flags); } /* Handle transmit complete interrupts */ if (sisr & SV_TCL) { spin_lock_irqsave (&ape->xmitlock, flags); atm_xmitint(ape, sisr); spin_unlock_irqrestore(&ape->xmitlock, flags); } /* Handle adapter error interrupts */ if (sisr & APE_ER) atm_error(ape, sisr); #ifdef FULL_STACK if (sisr & 0x80) /* No free frame buffers */ { spin_lock_irqsave (&ape->recvlock, flags); atm_addskbs(ape); spin_unlock_irqrestore(&ape->recvlock, flags); } #endif /* Read the performance counters every 10ms */ if (sisr & 0x1) atm_tick(ape, sisr); #ifdef CAPTURE_PMD atm_rdpmregs(ape); atm_incpmd(pmd.intcount); mask = 0x8000; for (i = 0; i < 16; i++) { if (sisr & mask) atm_incpmd(pmd.intcats[i]); mask >>= 1; } #endif /* Restore the MISR to allow more ints */ atm_wtsreg(ape, MISR, savemisr); return; } /**/ /* Module initialization entry point */ int init_module(void) { unsigned short vendor; unsigned short device; struct pci_dev *pci_dev; struct pci_dev *ibm_dev; unsigned char bus = 0; unsigned char devfn = 0; unsigned char wbus = 0; unsigned char wdevfn = 0; int rc; int i; int found = 0; unsigned char pci_cache; unsigned char pci_latency; struct pddtype *ape = &ape25; unsigned char hdr_type; unsigned char cfg_byte; int pci_index; if (!pcibios_present()) { printk("IBM APE25: No PCI Bios!\n"); return(-1); } printk("IBM APE25: Bios search for device \n"); pci_dev = NULL; while (pci_dev = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_ATM_APE25, pci_dev)) { ibm_dev = pci_dev; found += 1; } if (found != 1) { printk("IBM APE25: Found %d adapters \n" , found); printk("IBM APE25: Exactly ONE adapters is required \n"); return(-1); } bus = ibm_dev->bus->number; devfn = ibm_dev->devfn; printk("IBM APE25: Found 1 adapters at bus %d devfn %d \n", bus, devfn); /* Do pci configuration and then log PCI status */ rc = pci_write_config_word(ibm_dev, PCI_COMMAND, 0x117); rc = pci_write_config_word(ibm_dev, PCI_STATUS, 0xfd7f); rc = pci_write_config_byte(ibm_dev, PCI_LATENCY_TIMER, 0x40); ape->pci_bus = bus; ape->pci_devfn = devfn; ape->pci_irq = ibm_dev->irq; ape->pci_vendor = ibm_dev->vendor; ape->pci_device = ibm_dev->device; ape->pci_iobase = ibm_dev->resource[0].start; ape->pci_membase = (unsigned int *)ibm_dev->resource[0].start; rc = pci_read_config_byte(ibm_dev, PCI_REVISION_ID, &ape->pci_revid); rc |= pci_read_config_word(ibm_dev, PCI_COMMAND, &ape->pci_command); rc |= pci_read_config_word(ibm_dev, PCI_STATUS, &ape->pci_status); #if 0 rc |= pci_read_config_dword(ibm_dev, PCI_BASE_ADDRESS_0, &ape->pci_iobase); rc |= pci_read_config_dword(ibm_dev, PCI_BASE_ADDRESS_1, (int *)&ape->pci_membase); rc |= pci_read_config_byte(ibm_dev, PCI_CACHE_LINE_SIZE, &pci_cache); #endif rc |= pci_read_config_byte(ibm_dev, PCI_LATENCY_TIMER, &pci_latency); rc |= pci_read_config_byte(ibm_dev, PCI_HEADER_TYPE, &hdr_type); if (rc) { printk("APE25: PCI config read error on device %d.\n", device); return -EIO; } /* Low order bit indicates in I/O space.. */ ape->pci_iobase &= (~1); /* Allocate our IRQ */ /* free_irq(ape->pci_irq, ape); */ rc = request_irq(ape->pci_irq, ape25_irq, SA_INTERRUPT | SA_SHIRQ, "ape25", ape); if (rc) { printk("IBM APE25: Can't allocate IRQ %d. Code was %d \n", ape->pci_irq, rc); return -EIO; } WT_CNTLREG(ape, ERROR_REG, 0xffff); /* Do a soft reset */ WT_CNTLREG(ape, MODE_REG, RESET_MODE); i = 1; while (i > 0) { if (!(RD_CNTLREG(ape, MODE_REG) & RESET_MODE)) break; i += 1; } printk("IBM APE25: Reset complete at %d \n", i); WT_CNTLREG(ape, MODE_REG, OS2_MODE); /* Now IPL the pico processors */ atm_iplpp(ape); /* Allocate the DMA accessible buffer pools */ rc = atm_getdma(ape); if (rc) return(rc); #ifdef DEBUG_BASE printk("Back from get dma \n"); #endif /* Initialize the transmit buffer pool */ atm_initxb(ape); #ifdef DEBUG_BASE printk("Back from get initxb \n"); #endif /* Initialize the receiver buffer pool */ if (atm_initrb(ape)) return(-1); #ifdef DEBUG_BASE printk("Back from get initrb \n"); #endif /* Initialize the control/data local ram control structures */ atm_initxram(ape); #ifdef DEBUG_BASE printk("Back from get initxr \n"); #endif /* Initialize some miscellaneous regs */ atm_initregs(ape); #ifdef DEBUG_BASE printk("Back from get initrg \n"); #endif /* Print the configuration */ printk("IBM APE25: revid %x\n", ape->pci_revid); printk("IBM APE25: iobase %x\n", ape->pci_iobase); printk("IBM APE25: membase %x\n", ape->pci_membase); printk("IBM APE25: command %x\n", ape->pci_command); printk("IBM APE25: status %x\n", ape->pci_status); printk("IBM APE25: irq %x\n", ape->pci_irq); printk("IBM APE25: cache %x\n", pci_cache); printk("IBM APE25: hdrtype %x\n", hdr_type); printk("IBM APE25: latency %x\n", pci_latency); printk("IBM APE25: mode %x\n", RD_CNTLREG(ape, MODE_REG)); printk("IBM APE25: pmd at %x\n", &pmd); printk("IBM APE25: tbc at %x\n", ape->tbcount + 32); printk("PMD: %x %x %x %x \n", pmd.tclp0[0], pmd.tclp1[0], pmd.rclp0[0], pmd.rclp1[0]); /* Register as a bogo character device.. just to support easy */ /* ioctl access for testing. */ printk("IBM APE25: Module initialization\n"); rc = unregister_chrdev(APE25_MAJOR, "ape25"); if (rc = register_chrdev(APE25_MAJOR, "ape25", &atm_fops)) { printk( "APE25: cannot register; received %d\n", rc); return 1; } /* Finally init the interrupt mask reg, the transmit enable */ /* (ACONFIG) reg and turn on the board with the mode reg. */ /* This sets TMQ's 7-4 to low priority and 3-0 to high priority */ WT_CNTLREG(ape, ACONFIG_REG, 0x0fff); #if 1 WT_CNTLREG(ape, MODE_REG, OS2_MODE | PICO_ENABLE); #else WT_CNTLREG(ape, MODE_REG, INT_WRAP_MODE); for (i = 0; i < 1024 * 1024; i++); #endif #if 0 WT_CNTLREG(ape, MODE_REG, NORMAL_MODE); WT_CNTLREG(ape, MODE_REG, EXT_WRAP_MODE); WT_CNTLREG(ape, MODE_REG, LCLOCK_MODE); #endif /* Register ourselves with the Linux ATM stack */ #ifdef FULL_STACK ape25_register(ape, interface); #endif #ifdef CAPTURE_PMD ape->pmddata = &pmd; #endif atm_wtsreg(ape, MISR, 0xffff); #if 0 /* NVRAM TEST CODE */ printk("atm_wtsreg is at %x \n", atm_wtsreg); printk("atm_nvbuf is at %x \n", nvbuf); for (i = 0; i < 16; i++) atm_wtnvram(ape, 64 + i, i + 1); for (i = 0; i < 128; i++) { nvbuf[i] = atm_rdnvram(ape, i); } #endif #ifdef SYNTHETIC_DROPS ape->tqmax = 0x7fffffff; ape->dropthresh = 0x7fffffff; #endif spin_lock_init(&ape->xmitlock); spin_lock_init(&ape->recvlock); spin_lock_init(&ape->xramlock); printk("atm_rdnvram at %x \n", atm_rdnvram); printk("PMD: %x %x %x %x \n", pmd.tclp0[0], pmd.tclp1[0], pmd.rclp0[0], pmd.rclp1[0]); return(0); } /**/ /* Module termination entry point */ void cleanup_module(void) { struct pddtype *ape = &ape25; int rc; printk( "IBM APE25: Driver shutdown\n"); atm_wtsreg(ape, MISR, 0x0); WT_CNTLREG(ape, MODE_REG, 0); free_irq(ape->pci_irq, ape); rc = unregister_chrdev(APE25_MAJOR, "ape25"); printk("IBM APE25: Unregister device returned %d \n", rc); #ifdef FULL_STACK ape25_deregister(ape); #endif atm_freedma(ape); }