/* * File Name: * defxx.c * * Copyright Information: * Copyright Digital Equipment Corporation 1996. * * This software may be used and distributed according to the terms of * the GNU General Public License, incorporated herein by reference. * * Abstract: * A Linux device driver supporting the Digital Equipment Corporation * FDDI EISA and PCI controller families. Supported adapters include: * * DEC FDDIcontroller/EISA (DEFEA) * DEC FDDIcontroller/PCI (DEFPA) * * The original author: * LVS Lawrence V. Stefani * * Maintainers: * macro Maciej W. Rozycki * * Credits: * I'd like to thank Patricia Cross for helping me get started with * Linux, David Davies for a lot of help upgrading and configuring * my development system and for answering many OS and driver * development questions, and Alan Cox for recommendations and * integration help on getting FDDI support into Linux. LVS * * Driver Architecture: * The driver architecture is largely based on previous driver work * for other operating systems. The upper edge interface and * functions were largely taken from existing Linux device drivers * such as David Davies' DE4X5.C driver and Donald Becker's TULIP.C * driver. * * Adapter Probe - * The driver scans for supported EISA adapters by reading the * SLOT ID register for each EISA slot and making a match * against the expected value. * * Bus-Specific Initialization - * This driver currently supports both EISA and PCI controller * families. While the custom DMA chip and FDDI logic is similar * or identical, the bus logic is very different. After * initialization, the only bus-specific differences is in how the * driver enables and disables interrupts. Other than that, the * run-time critical code behaves the same on both families. * It's important to note that both adapter families are configured * to I/O map, rather than memory map, the adapter registers. * * Driver Open/Close - * In the driver open routine, the driver ISR (interrupt service * routine) is registered and the adapter is brought to an * operational state. In the driver close routine, the opposite * occurs; the driver ISR is deregistered and the adapter is * brought to a safe, but closed state. Users may use consecutive * commands to bring the adapter up and down as in the following * example: * ifconfig fddi0 up * ifconfig fddi0 down * ifconfig fddi0 up * * Driver Shutdown - * Apparently, there is no shutdown or halt routine support under * Linux. This routine would be called during "reboot" or * "shutdown" to allow the driver to place the adapter in a safe * state before a warm reboot occurs. To be really safe, the user * should close the adapter before shutdown (eg. ifconfig fddi0 down) * to ensure that the adapter DMA engine is taken off-line. However, * the current driver code anticipates this problem and always issues * a soft reset of the adapter at the beginning of driver initialization. * A future driver enhancement in this area may occur in 2.1.X where * Alan indicated that a shutdown handler may be implemented. * * Interrupt Service Routine - * The driver supports shared interrupts, so the ISR is registered for * each board with the appropriate flag and the pointer to that board's * device structure. This provides the context during interrupt * processing to support shared interrupts and multiple boards. * * Interrupt enabling/disabling can occur at many levels. At the host * end, you can disable system interrupts, or disable interrupts at the * PIC (on Intel systems). Across the bus, both EISA and PCI adapters * have a bus-logic chip interrupt enable/disable as well as a DMA * controller interrupt enable/disable. * * The driver currently enables and disables adapter interrupts at the * bus-logic chip and assumes that Linux will take care of clearing or * acknowledging any host-based interrupt chips. * * Control Functions - * Control functions are those used to support functions such as adding * or deleting multicast addresses, enabling or disabling packet * reception filters, or other custom/proprietary commands. Presently, * the driver supports the "get statistics", "set multicast list", and * "set mac address" functions defined by Linux. A list of possible * enhancements include: * * - Custom ioctl interface for executing port interface commands * - Custom ioctl interface for adding unicast addresses to * adapter CAM (to support bridge functions). * - Custom ioctl interface for supporting firmware upgrades. * * Hardware (port interface) Support Routines - * The driver function names that start with "dfx_hw_" represent * low-level port interface routines that are called frequently. They * include issuing a DMA or port control command to the adapter, * resetting the adapter, or reading the adapter state. Since the * driver initialization and run-time code must make calls into the * port interface, these routines were written to be as generic and * usable as possible. * * Receive Path - * The adapter DMA engine supports a 256 entry receive descriptor block * of which up to 255 entries can be used at any given time. The * architecture is a standard producer, consumer, completion model in * which the driver "produces" receive buffers to the adapter, the * adapter "consumes" the receive buffers by DMAing incoming packet data, * and the driver "completes" the receive buffers by servicing the * incoming packet, then "produces" a new buffer and starts the cycle * again. Receive buffers can be fragmented in up to 16 fragments * (descriptor entries). For simplicity, this driver posts * single-fragment receive buffers of 4608 bytes, then allocates a * sk_buff, copies the data, then reposts the buffer. To reduce CPU * utilization, a better approach would be to pass up the receive * buffer (no extra copy) then allocate and post a replacement buffer. * This is a performance enhancement that should be looked into at * some point. * * Transmit Path - * Like the receive path, the adapter DMA engine supports a 256 entry * transmit descriptor block of which up to 255 entries can be used at * any given time. Transmit buffers can be fragmented in up to 255 * fragments (descriptor entries). This driver always posts one * fragment per transmit packet request. * * The fragment contains the entire packet from FC to end of data. * Before posting the buffer to the adapter, the driver sets a three-byte * packet request header (PRH) which is required by the Motorola MAC chip * used on the adapters. The PRH tells the MAC the type of token to * receive/send, whether or not to generate and append the CRC, whether * synchronous or asynchronous framing is used, etc. Since the PRH * definition is not necessarily consistent across all FDDI chipsets, * the driver, rather than the common FDDI packet handler routines, * sets these bytes. * * To reduce the amount of descriptor fetches needed per transmit request, * the driver takes advantage of the fact that there are at least three * bytes available before the skb->data field on the outgoing transmit * request. This is guaranteed by having fddi_setup() in net_init.c set * dev->hard_header_len to 24 bytes. 21 bytes accounts for the largest * header in an 802.2 SNAP frame. The other 3 bytes are the extra "pad" * bytes which we'll use to store the PRH. * * There's a subtle advantage to adding these pad bytes to the * hard_header_len, it ensures that the data portion of the packet for * an 802.2 SNAP frame is longword aligned. Other FDDI driver * implementations may not need the extra padding and can start copying * or DMAing directly from the FC byte which starts at skb->data. Should * another driver implementation need ADDITIONAL padding, the net_init.c * module should be updated and dev->hard_header_len should be increased. * NOTE: To maintain the alignment on the data portion of the packet, * dev->hard_header_len should always be evenly divisible by 4 and at * least 24 bytes in size. * * Modification History: * Date Name Description * 16-Aug-96 LVS Created. * 20-Aug-96 LVS Updated dfx_probe so that version information * string is only displayed if 1 or more cards are * found. Changed dfx_rcv_queue_process to copy * 3 NULL bytes before FC to ensure that data is * longword aligned in receive buffer. * 09-Sep-96 LVS Updated dfx_ctl_set_multicast_list to enable * LLC group promiscuous mode if multicast list * is too large. LLC individual/group promiscuous * mode is now disabled if IFF_PROMISC flag not set. * dfx_xmt_queue_pkt no longer checks for NULL skb * on Alan Cox recommendation. Added node address * override support. * 12-Sep-96 LVS Reset current address to factory address during * device open. Updated transmit path to post a * single fragment which includes PRH->end of data. * Mar 2000 AC Did various cleanups for 2.3.x * Jun 2000 jgarzik PCI and resource alloc cleanups * Jul 2000 tjeerd Much cleanup and some bug fixes * Sep 2000 tjeerd Fix leak on unload, cosmetic code cleanup * Feb 2001 Skb allocation fixes * Feb 2001 davej PCI enable cleanups. * 04 Aug 2003 macro Converted to the DMA API. * 14 Aug 2004 macro Fix device names reported. * 14 Jun 2005 macro Use irqreturn_t. */ /* Include files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defxx.h" /* Version information string should be updated prior to each new release! */ #define DRV_NAME "defxx" #define DRV_VERSION "v1.08" #define DRV_RELDATE "2005/06/14" static char version[] __devinitdata = DRV_NAME ": " DRV_VERSION " " DRV_RELDATE " Lawrence V. Stefani and others\n"; #define DYNAMIC_BUFFERS 1 #define SKBUFF_RX_COPYBREAK 200 /* * NEW_SKB_SIZE = PI_RCV_DATA_K_SIZE_MAX+128 to allow 128 byte * alignment for compatibility with old EISA boards. */ #define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX+128) /* Define module-wide (static) routines */ static void dfx_bus_init(struct net_device *dev); static void dfx_bus_config_check(DFX_board_t *bp); static int dfx_driver_init(struct net_device *dev, const char *print_name); static int dfx_adap_init(DFX_board_t *bp, int get_buffers); static int dfx_open(struct net_device *dev); static int dfx_close(struct net_device *dev); static void dfx_int_pr_halt_id(DFX_board_t *bp); static void dfx_int_type_0_process(DFX_board_t *bp); static void dfx_int_common(struct net_device *dev); static irqreturn_t dfx_interrupt(int irq, void *dev_id, struct pt_regs *regs); static struct net_device_stats *dfx_ctl_get_stats(struct net_device *dev); static void dfx_ctl_set_multicast_list(struct net_device *dev); static int dfx_ctl_set_mac_address(struct net_device *dev, void *addr); static int dfx_ctl_update_cam(DFX_board_t *bp); static int dfx_ctl_update_filters(DFX_board_t *bp); static int dfx_hw_dma_cmd_req(DFX_board_t *bp); static int dfx_hw_port_ctrl_req(DFX_board_t *bp, PI_UINT32 command, PI_UINT32 data_a, PI_UINT32 data_b, PI_UINT32 *host_data); static void dfx_hw_adap_reset(DFX_board_t *bp, PI_UINT32 type); static int dfx_hw_adap_state_rd(DFX_board_t *bp); static int dfx_hw_dma_uninit(DFX_board_t *bp, PI_UINT32 type); static int dfx_rcv_init(DFX_board_t *bp, int get_buffers); static void dfx_rcv_queue_process(DFX_board_t *bp); static void dfx_rcv_flush(DFX_board_t *bp); static int dfx_xmt_queue_pkt(struct sk_buff *skb, struct net_device *dev); static int dfx_xmt_done(DFX_board_t *bp); static void dfx_xmt_flush(DFX_board_t *bp); /* Define module-wide (static) variables */ static struct net_device *root_dfx_eisa_dev; /* * ======================= * = dfx_port_write_byte = * = dfx_port_read_byte = * = dfx_port_write_long = * = dfx_port_read_long = * ======================= * * Overview: * Routines for reading and writing values from/to adapter * * Returns: * None * * Arguments: * bp - pointer to board information * offset - register offset from base I/O address * data - for dfx_port_write_byte and dfx_port_write_long, this * is a value to write. * for dfx_port_read_byte and dfx_port_read_byte, this * is a pointer to store the read value. * * Functional Description: * These routines perform the correct operation to read or write * the adapter register. * * EISA port block base addresses are based on the slot number in which the * controller is installed. For example, if the EISA controller is installed * in slot 4, the port block base address is 0x4000. If the controller is * installed in slot 2, the port block base address is 0x2000, and so on. * This port block can be used to access PDQ, ESIC, and DEFEA on-board * registers using the register offsets defined in DEFXX.H. * * PCI port block base addresses are assigned by the PCI BIOS or system * firmware. There is one 128 byte port block which can be accessed. It * allows for I/O mapping of both PDQ and PFI registers using the register * offsets defined in DEFXX.H. * * Return Codes: * None * * Assumptions: * bp->base_addr is a valid base I/O address for this adapter. * offset is a valid register offset for this adapter. * * Side Effects: * Rather than produce macros for these functions, these routines * are defined using "inline" to ensure that the compiler will * generate inline code and not waste a procedure call and return. * This provides all the benefits of macros, but with the * advantage of strict data type checking. */ static inline void dfx_port_write_byte( DFX_board_t *bp, int offset, u8 data ) { u16 port = bp->base_addr + offset; outb(data, port); } static inline void dfx_port_read_byte( DFX_board_t *bp, int offset, u8 *data ) { u16 port = bp->base_addr + offset; *data = inb(port); } static inline void dfx_port_write_long( DFX_board_t *bp, int offset, u32 data ) { u16 port = bp->base_addr + offset; outl(data, port); } static inline void dfx_port_read_long( DFX_board_t *bp, int offset, u32 *data ) { u16 port = bp->base_addr + offset; *data = inl(port); } /* * ============= * = dfx_init_one_pci_or_eisa = * ============= * * Overview: * Initializes a supported FDDI EISA or PCI controller * * Returns: * Condition code * * Arguments: * pdev - pointer to pci device information (NULL for EISA) * ioaddr - pointer to port (NULL for PCI) * * Functional Description: * * Return Codes: * 0 - This device (fddi0, fddi1, etc) configured successfully * -EBUSY - Failed to get resources, or dfx_driver_init failed. * * Assumptions: * It compiles so it should work :-( (PCI cards do :-) * * Side Effects: * Device structures for FDDI adapters (fddi0, fddi1, etc) are * initialized and the board resources are read and stored in * the device structure. */ static int __devinit dfx_init_one_pci_or_eisa(struct pci_dev *pdev, long ioaddr) { static int version_disp; char *print_name = DRV_NAME; struct net_device *dev; DFX_board_t *bp; /* board pointer */ int alloc_size; /* total buffer size used */ int err; if (!version_disp) { /* display version info if adapter is found */ version_disp = 1; /* set display flag to TRUE so that */ printk(version); /* we only display this string ONCE */ } if (pdev != NULL) print_name = pci_name(pdev); dev = alloc_fddidev(sizeof(*bp)); if (!dev) { printk(KERN_ERR "%s: unable to allocate fddidev, aborting\n", print_name); return -ENOMEM; } /* Enable PCI device. */ if (pdev != NULL) { err = pci_enable_device (pdev); if (err) goto err_out; ioaddr = pci_resource_start (pdev, 1); } SET_MODULE_OWNER(dev); if (pdev != NULL) SET_NETDEV_DEV(dev, &pdev->dev); bp = dev->priv; if (!request_region(ioaddr, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, print_name)) { printk(KERN_ERR "%s: Cannot reserve I/O resource " "0x%x @ 0x%lx, aborting\n", print_name, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, ioaddr); err = -EBUSY; goto err_out; } /* Initialize new device structure */ dev->base_addr = ioaddr; /* save port (I/O) base address */ dev->get_stats = dfx_ctl_get_stats; dev->open = dfx_open; dev->stop = dfx_close; dev->hard_start_xmit = dfx_xmt_queue_pkt; dev->set_multicast_list = dfx_ctl_set_multicast_list; dev->set_mac_address = dfx_ctl_set_mac_address; if (pdev == NULL) { /* EISA board */ bp->bus_type = DFX_BUS_TYPE_EISA; bp->next = root_dfx_eisa_dev; root_dfx_eisa_dev = dev; } else { /* PCI board */ bp->bus_type = DFX_BUS_TYPE_PCI; bp->pci_dev = pdev; pci_set_drvdata (pdev, dev); pci_set_master (pdev); } if (dfx_driver_init(dev, print_name) != DFX_K_SUCCESS) { err = -ENODEV; goto err_out_region; } err = register_netdev(dev); if (err) goto err_out_kfree; printk("%s: registered as %s\n", print_name, dev->name); return 0; err_out_kfree: alloc_size = sizeof(PI_DESCR_BLOCK) + PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX + #ifndef DYNAMIC_BUFFERS (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) + #endif sizeof(PI_CONSUMER_BLOCK) + (PI_ALIGN_K_DESC_BLK - 1); if (bp->kmalloced) pci_free_consistent(pdev, alloc_size, bp->kmalloced, bp->kmalloced_dma); err_out_region: release_region(ioaddr, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN); err_out: free_netdev(dev); return err; } static int __devinit dfx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { return dfx_init_one_pci_or_eisa(pdev, 0); } static int __init dfx_eisa_init(void) { int rc = -ENODEV; int i; /* used in for loops */ u16 port; /* temporary I/O (port) address */ u32 slot_id; /* EISA hardware (slot) ID read from adapter */ DBG_printk("In dfx_eisa_init...\n"); /* Scan for FDDI EISA controllers */ for (i=0; i < DFX_MAX_EISA_SLOTS; i++) /* only scan for up to 16 EISA slots */ { port = (i << 12) + PI_ESIC_K_SLOT_ID; /* port = I/O address for reading slot ID */ slot_id = inl(port); /* read EISA HW (slot) ID */ if ((slot_id & 0xF0FFFFFF) == DEFEA_PRODUCT_ID) { port = (i << 12); /* recalc base addr */ if (dfx_init_one_pci_or_eisa(NULL, port) == 0) rc = 0; } } return rc; } /* * ================ * = dfx_bus_init = * ================ * * Overview: * Initializes EISA and PCI controller bus-specific logic. * * Returns: * None * * Arguments: * dev - pointer to device information * * Functional Description: * Determine and save adapter IRQ in device table, * then perform bus-specific logic initialization. * * Return Codes: * None * * Assumptions: * dev->base_addr has already been set with the proper * base I/O address for this device. * * Side Effects: * Interrupts are enabled at the adapter bus-specific logic. * Note: Interrupts at the DMA engine (PDQ chip) are not * enabled yet. */ static void __devinit dfx_bus_init(struct net_device *dev) { DFX_board_t *bp = dev->priv; u8 val; /* used for I/O read/writes */ DBG_printk("In dfx_bus_init...\n"); /* * Initialize base I/O address field in bp structure * * Note: bp->base_addr is the same as dev->base_addr. * It's useful because often we'll need to read * or write registers where we already have the * bp pointer instead of the dev pointer. Having * the base address in the bp structure will * save a pointer dereference. * * IMPORTANT!! This field must be defined before * any of the dfx_port_* inline functions are * called. */ bp->base_addr = dev->base_addr; /* And a pointer back to the net_device struct */ bp->dev = dev; /* Initialize adapter based on bus type */ if (bp->bus_type == DFX_BUS_TYPE_EISA) { /* Get the interrupt level from the ESIC chip */ dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &val); switch ((val & PI_CONFIG_STAT_0_M_IRQ) >> PI_CONFIG_STAT_0_V_IRQ) { case PI_CONFIG_STAT_0_IRQ_K_9: dev->irq = 9; break; case PI_CONFIG_STAT_0_IRQ_K_10: dev->irq = 10; break; case PI_CONFIG_STAT_0_IRQ_K_11: dev->irq = 11; break; case PI_CONFIG_STAT_0_IRQ_K_15: dev->irq = 15; break; } /* Enable access to I/O on the board by writing 0x03 to Function Control Register */ dfx_port_write_byte(bp, PI_ESIC_K_FUNCTION_CNTRL, PI_ESIC_K_FUNCTION_CNTRL_IO_ENB); /* Set the I/O decode range of the board */ val = ((dev->base_addr >> 12) << PI_IO_CMP_V_SLOT); dfx_port_write_byte(bp, PI_ESIC_K_IO_CMP_0_1, val); dfx_port_write_byte(bp, PI_ESIC_K_IO_CMP_1_1, val); /* Enable access to rest of module (including PDQ and packet memory) */ dfx_port_write_byte(bp, PI_ESIC_K_SLOT_CNTRL, PI_SLOT_CNTRL_M_ENB); /* * Map PDQ registers into I/O space. This is done by clearing a bit * in Burst Holdoff register. */ dfx_port_read_byte(bp, PI_ESIC_K_BURST_HOLDOFF, &val); dfx_port_write_byte(bp, PI_ESIC_K_BURST_HOLDOFF, (val & ~PI_BURST_HOLDOFF_M_MEM_MAP)); /* Enable interrupts at EISA bus interface chip (ESIC) */ dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &val); dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, (val | PI_CONFIG_STAT_0_M_INT_ENB)); } else { struct pci_dev *pdev = bp->pci_dev; /* Get the interrupt level from the PCI Configuration Table */ dev->irq = pdev->irq; /* Check Latency Timer and set if less than minimal */ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &val); if (val < PFI_K_LAT_TIMER_MIN) /* if less than min, override with default */ { val = PFI_K_LAT_TIMER_DEF; pci_write_config_byte(pdev, PCI_LATENCY_TIMER, val); } /* Enable interrupts at PCI bus interface chip (PFI) */ dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, (PFI_MODE_M_PDQ_INT_ENB | PFI_MODE_M_DMA_ENB)); } } /* * ======================== * = dfx_bus_config_check = * ======================== * * Overview: * Checks the configuration (burst size, full-duplex, etc.) If any parameters * are illegal, then this routine will set new defaults. * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * For Revision 1 FDDI EISA, Revision 2 or later FDDI EISA with rev E or later * PDQ, and all FDDI PCI controllers, all values are legal. * * Return Codes: * None * * Assumptions: * dfx_adap_init has NOT been called yet so burst size and other items have * not been set. * * Side Effects: * None */ static void __devinit dfx_bus_config_check(DFX_board_t *bp) { int status; /* return code from adapter port control call */ u32 slot_id; /* EISA-bus hardware id (DEC3001, DEC3002,...) */ u32 host_data; /* LW data returned from port control call */ DBG_printk("In dfx_bus_config_check...\n"); /* Configuration check only valid for EISA adapter */ if (bp->bus_type == DFX_BUS_TYPE_EISA) { dfx_port_read_long(bp, PI_ESIC_K_SLOT_ID, &slot_id); /* * First check if revision 2 EISA controller. Rev. 1 cards used * PDQ revision B, so no workaround needed in this case. Rev. 3 * cards used PDQ revision E, so no workaround needed in this * case, either. Only Rev. 2 cards used either Rev. D or E * chips, so we must verify the chip revision on Rev. 2 cards. */ if (slot_id == DEFEA_PROD_ID_2) { /* * Revision 2 FDDI EISA controller found, so let's check PDQ * revision of adapter. */ status = dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_SUB_CMD, PI_SUB_CMD_K_PDQ_REV_GET, 0, &host_data); if ((status != DFX_K_SUCCESS) || (host_data == 2)) { /* * Either we couldn't determine the PDQ revision, or * we determined that it is at revision D. In either case, * we need to implement the workaround. */ /* Ensure that the burst size is set to 8 longwords or less */ switch (bp->burst_size) { case PI_PDATA_B_DMA_BURST_SIZE_32: case PI_PDATA_B_DMA_BURST_SIZE_16: bp->burst_size = PI_PDATA_B_DMA_BURST_SIZE_8; break; default: break; } /* Ensure that full-duplex mode is not enabled */ bp->full_duplex_enb = PI_SNMP_K_FALSE; } } } } /* * =================== * = dfx_driver_init = * =================== * * Overview: * Initializes remaining adapter board structure information * and makes sure adapter is in a safe state prior to dfx_open(). * * Returns: * Condition code * * Arguments: * dev - pointer to device information * print_name - printable device name * * Functional Description: * This function allocates additional resources such as the host memory * blocks needed by the adapter (eg. descriptor and consumer blocks). * Remaining bus initialization steps are also completed. The adapter * is also reset so that it is in the DMA_UNAVAILABLE state. The OS * must call dfx_open() to open the adapter and bring it on-line. * * Return Codes: * DFX_K_SUCCESS - initialization succeeded * DFX_K_FAILURE - initialization failed - could not allocate memory * or read adapter MAC address * * Assumptions: * Memory allocated from pci_alloc_consistent() call is physically * contiguous, locked memory. * * Side Effects: * Adapter is reset and should be in DMA_UNAVAILABLE state before * returning from this routine. */ static int __devinit dfx_driver_init(struct net_device *dev, const char *print_name) { DFX_board_t *bp = dev->priv; int alloc_size; /* total buffer size needed */ char *top_v, *curr_v; /* virtual addrs into memory block */ dma_addr_t top_p, curr_p; /* physical addrs into memory block */ u32 data; /* host data register value */ DBG_printk("In dfx_driver_init...\n"); /* Initialize bus-specific hardware registers */ dfx_bus_init(dev); /* * Initialize default values for configurable parameters * * Note: All of these parameters are ones that a user may * want to customize. It'd be nice to break these * out into Space.c or someplace else that's more * accessible/understandable than this file. */ bp->full_duplex_enb = PI_SNMP_K_FALSE; bp->req_ttrt = 8 * 12500; /* 8ms in 80 nanosec units */ bp->burst_size = PI_PDATA_B_DMA_BURST_SIZE_DEF; bp->rcv_bufs_to_post = RCV_BUFS_DEF; /* * Ensure that HW configuration is OK * * Note: Depending on the hardware revision, we may need to modify * some of the configurable parameters to workaround hardware * limitations. We'll perform this configuration check AFTER * setting the parameters to their default values. */ dfx_bus_config_check(bp); /* Disable PDQ interrupts first */ dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST); /* Read the factory MAC address from the adapter then save it */ if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_LO, 0, &data) != DFX_K_SUCCESS) { printk("%s: Could not read adapter factory MAC address!\n", print_name); return(DFX_K_FAILURE); } memcpy(&bp->factory_mac_addr[0], &data, sizeof(u32)); if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_HI, 0, &data) != DFX_K_SUCCESS) { printk("%s: Could not read adapter factory MAC address!\n", print_name); return(DFX_K_FAILURE); } memcpy(&bp->factory_mac_addr[4], &data, sizeof(u16)); /* * Set current address to factory address * * Note: Node address override support is handled through * dfx_ctl_set_mac_address. */ memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN); if (bp->bus_type == DFX_BUS_TYPE_EISA) printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, " "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", print_name, dev->base_addr, dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); else printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, " "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", print_name, dev->base_addr, dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); /* * Get memory for descriptor block, consumer block, and other buffers * that need to be DMA read or written to by the adapter. */ alloc_size = sizeof(PI_DESCR_BLOCK) + PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX + #ifndef DYNAMIC_BUFFERS (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) + #endif sizeof(PI_CONSUMER_BLOCK) + (PI_ALIGN_K_DESC_BLK - 1); bp->kmalloced = top_v = pci_alloc_consistent(bp->pci_dev, alloc_size, &bp->kmalloced_dma); if (top_v == NULL) { printk("%s: Could not allocate memory for host buffers " "and structures!\n", print_name); return(DFX_K_FAILURE); } memset(top_v, 0, alloc_size); /* zero out memory before continuing */ top_p = bp->kmalloced_dma; /* get physical address of buffer */ /* * To guarantee the 8K alignment required for the descriptor block, 8K - 1 * plus the amount of memory needed was allocated. The physical address * is now 8K aligned. By carving up the memory in a specific order, * we'll guarantee the alignment requirements for all other structures. * * Note: If the assumptions change regarding the non-paged, non-cached, * physically contiguous nature of the memory block or the address * alignments, then we'll need to implement a different algorithm * for allocating the needed memory. */ curr_p = ALIGN(top_p, PI_ALIGN_K_DESC_BLK); curr_v = top_v + (curr_p - top_p); /* Reserve space for descriptor block */ bp->descr_block_virt = (PI_DESCR_BLOCK *) curr_v; bp->descr_block_phys = curr_p; curr_v += sizeof(PI_DESCR_BLOCK); curr_p += sizeof(PI_DESCR_BLOCK); /* Reserve space for command request buffer */ bp->cmd_req_virt = (PI_DMA_CMD_REQ *) curr_v; bp->cmd_req_phys = curr_p; curr_v += PI_CMD_REQ_K_SIZE_MAX; curr_p += PI_CMD_REQ_K_SIZE_MAX; /* Reserve space for command response buffer */ bp->cmd_rsp_virt = (PI_DMA_CMD_RSP *) curr_v; bp->cmd_rsp_phys = curr_p; curr_v += PI_CMD_RSP_K_SIZE_MAX; curr_p += PI_CMD_RSP_K_SIZE_MAX; /* Reserve space for the LLC host receive queue buffers */ bp->rcv_block_virt = curr_v; bp->rcv_block_phys = curr_p; #ifndef DYNAMIC_BUFFERS curr_v += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); curr_p += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); #endif /* Reserve space for the consumer block */ bp->cons_block_virt = (PI_CONSUMER_BLOCK *) curr_v; bp->cons_block_phys = curr_p; /* Display virtual and physical addresses if debug driver */ DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n", print_name, (long)bp->descr_block_virt, bp->descr_block_phys); DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n", print_name, (long)bp->cmd_req_virt, bp->cmd_req_phys); DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n", print_name, (long)bp->cmd_rsp_virt, bp->cmd_rsp_phys); DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n", print_name, (long)bp->rcv_block_virt, bp->rcv_block_phys); DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n", print_name, (long)bp->cons_block_virt, bp->cons_block_phys); return(DFX_K_SUCCESS); } /* * ================= * = dfx_adap_init = * ================= * * Overview: * Brings the adapter to the link avail/link unavailable state. * * Returns: * Condition code * * Arguments: * bp - pointer to board information * get_buffers - non-zero if buffers to be allocated * * Functional Description: * Issues the low-level firmware/hardware calls necessary to bring * the adapter up, or to properly reset and restore adapter during * run-time. * * Return Codes: * DFX_K_SUCCESS - Adapter brought up successfully * DFX_K_FAILURE - Adapter initialization failed * * Assumptions: * bp->reset_type should be set to a valid reset type value before * calling this routine. * * Side Effects: * Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state * upon a successful return of this routine. */ static int dfx_adap_init(DFX_board_t *bp, int get_buffers) { DBG_printk("In dfx_adap_init...\n"); /* Disable PDQ interrupts first */ dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ if (dfx_hw_dma_uninit(bp, bp->reset_type) != DFX_K_SUCCESS) { printk("%s: Could not uninitialize/reset adapter!\n", bp->dev->name); return(DFX_K_FAILURE); } /* * When the PDQ is reset, some false Type 0 interrupts may be pending, * so we'll acknowledge all Type 0 interrupts now before continuing. */ dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, PI_HOST_INT_K_ACK_ALL_TYPE_0); /* * Clear Type 1 and Type 2 registers before going to DMA_AVAILABLE state * * Note: We only need to clear host copies of these registers. The PDQ reset * takes care of the on-board register values. */ bp->cmd_req_reg.lword = 0; bp->cmd_rsp_reg.lword = 0; bp->rcv_xmt_reg.lword = 0; /* Clear consumer block before going to DMA_AVAILABLE state */ memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK)); /* Initialize the DMA Burst Size */ if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_SUB_CMD, PI_SUB_CMD_K_BURST_SIZE_SET, bp->burst_size, NULL) != DFX_K_SUCCESS) { printk("%s: Could not set adapter burst size!\n", bp->dev->name); return(DFX_K_FAILURE); } /* * Set base address of Consumer Block * * Assumption: 32-bit physical address of consumer block is 64 byte * aligned. That is, bits 0-5 of the address must be zero. */ if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_CONS_BLOCK, bp->cons_block_phys, 0, NULL) != DFX_K_SUCCESS) { printk("%s: Could not set consumer block address!\n", bp->dev->name); return(DFX_K_FAILURE); } /* * Set base address of Descriptor Block and bring adapter to DMA_AVAILABLE state * * Note: We also set the literal and data swapping requirements in this * command. Since this driver presently runs on Intel platforms * which are Little Endian, we'll tell the adapter to byte swap * data only. This code will need to change when we support * Big Endian systems (eg. PowerPC). * * Assumption: 32-bit physical address of descriptor block is 8Kbyte * aligned. That is, bits 0-12 of the address must be zero. */ if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_INIT, (u32) (bp->descr_block_phys | PI_PDATA_A_INIT_M_BSWAP_DATA), 0, NULL) != DFX_K_SUCCESS) { printk("%s: Could not set descriptor block address!\n", bp->dev->name); return(DFX_K_FAILURE); } /* Set transmit flush timeout value */ bp->cmd_req_virt->cmd_type = PI_CMD_K_CHARS_SET; bp->cmd_req_virt->char_set.item[0].item_code = PI_ITEM_K_FLUSH_TIME; bp->cmd_req_virt->char_set.item[0].value = 3; /* 3 seconds */ bp->cmd_req_virt->char_set.item[0].item_index = 0; bp->cmd_req_virt->char_set.item[1].item_code = PI_ITEM_K_EOL; if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) { printk("%s: DMA command request failed!\n", bp->dev->name); return(DFX_K_FAILURE); } /* Set the initial values for eFDXEnable and MACTReq MIB objects */ bp->cmd_req_virt->cmd_type = PI_CMD_K_SNMP_SET; bp->cmd_req_virt->snmp_set.item[0].item_code = PI_ITEM_K_FDX_ENB_DIS; bp->cmd_req_virt->snmp_set.item[0].value = bp->full_duplex_enb; bp->cmd_req_virt->snmp_set.item[0].item_index = 0; bp->cmd_req_virt->snmp_set.item[1].item_code = PI_ITEM_K_MAC_T_REQ; bp->cmd_req_virt->snmp_set.item[1].value = bp->req_ttrt; bp->cmd_req_virt->snmp_set.item[1].item_index = 0; bp->cmd_req_virt->snmp_set.item[2].item_code = PI_ITEM_K_EOL; if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) { printk("%s: DMA command request failed!\n", bp->dev->name); return(DFX_K_FAILURE); } /* Initialize adapter CAM */ if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS) { printk("%s: Adapter CAM update failed!\n", bp->dev->name); return(DFX_K_FAILURE); } /* Initialize adapter filters */ if (dfx_ctl_update_filters(bp) != DFX_K_SUCCESS) { printk("%s: Adapter filters update failed!\n", bp->dev->name); return(DFX_K_FAILURE); } /* * Remove any existing dynamic buffers (i.e. if the adapter is being * reinitialized) */ if (get_buffers) dfx_rcv_flush(bp); /* Initialize receive descriptor block and produce buffers */ if (dfx_rcv_init(bp, get_buffers)) { printk("%s: Receive buffer allocation failed\n", bp->dev->name); if (get_buffers) dfx_rcv_flush(bp); return(DFX_K_FAILURE); } /* Issue START command and bring adapter to LINK_(UN)AVAILABLE state */ bp->cmd_req_virt->cmd_type = PI_CMD_K_START; if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) { printk("%s: Start command failed\n", bp->dev->name); if (get_buffers) dfx_rcv_flush(bp); return(DFX_K_FAILURE); } /* Initialization succeeded, reenable PDQ interrupts */ dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_ENABLE_DEF_INTS); return(DFX_K_SUCCESS); } /* * ============ * = dfx_open = * ============ * * Overview: * Opens the adapter * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This function brings the adapter to an operational state. * * Return Codes: * 0 - Adapter was successfully opened * -EAGAIN - Could not register IRQ or adapter initialization failed * * Assumptions: * This routine should only be called for a device that was * initialized successfully. * * Side Effects: * Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state * if the open is successful. */ static int dfx_open(struct net_device *dev) { int ret; DFX_board_t *bp = dev->priv; DBG_printk("In dfx_open...\n"); /* Register IRQ - support shared interrupts by passing device ptr */ ret = request_irq(dev->irq, dfx_interrupt, SA_SHIRQ, dev->name, dev); if (ret) { printk(KERN_ERR "%s: Requested IRQ %d is busy\n", dev->name, dev->irq); return ret; } /* * Set current address to factory MAC address * * Note: We've already done this step in dfx_driver_init. * However, it's possible that a user has set a node * address override, then closed and reopened the * adapter. Unless we reset the device address field * now, we'll continue to use the existing modified * address. */ memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN); /* Clear local unicast/multicast address tables and counts */ memset(bp->uc_table, 0, sizeof(bp->uc_table)); memset(bp->mc_table, 0, sizeof(bp->mc_table)); bp->uc_count = 0; bp->mc_count = 0; /* Disable promiscuous filter settings */ bp->ind_group_prom = PI_FSTATE_K_BLOCK; bp->group_prom = PI_FSTATE_K_BLOCK; spin_lock_init(&bp->lock); /* Reset and initialize adapter */ bp->reset_type = PI_PDATA_A_RESET_M_SKIP_ST; /* skip self-test */ if (dfx_adap_init(bp, 1) != DFX_K_SUCCESS) { printk(KERN_ERR "%s: Adapter open failed!\n", dev->name); free_irq(dev->irq, dev); return -EAGAIN; } /* Set device structure info */ netif_start_queue(dev); return(0); } /* * ============= * = dfx_close = * ============= * * Overview: * Closes the device/module. * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This routine closes the adapter and brings it to a safe state. * The interrupt service routine is deregistered with the OS. * The adapter can be opened again with another call to dfx_open(). * * Return Codes: * Always return 0. * * Assumptions: * No further requests for this adapter are made after this routine is * called. dfx_open() can be called to reset and reinitialize the * adapter. * * Side Effects: * Adapter should be in DMA_UNAVAILABLE state upon completion of this * routine. */ static int dfx_close(struct net_device *dev) { DFX_board_t *bp = dev->priv; DBG_printk("In dfx_close...\n"); /* Disable PDQ interrupts first */ dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST); /* * Flush any pending transmit buffers * * Note: It's important that we flush the transmit buffers * BEFORE we clear our copy of the Type 2 register. * Otherwise, we'll have no idea how many buffers * we need to free. */ dfx_xmt_flush(bp); /* * Clear Type 1 and Type 2 registers after adapter reset * * Note: Even though we're closing the adapter, it's * possible that an interrupt will occur after * dfx_close is called. Without some assurance to * the contrary we want to make sure that we don't * process receive and transmit LLC frames and update * the Type 2 register with bad information. */ bp->cmd_req_reg.lword = 0; bp->cmd_rsp_reg.lword = 0; bp->rcv_xmt_reg.lword = 0; /* Clear consumer block for the same reason given above */ memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK)); /* Release all dynamically allocate skb in the receive ring. */ dfx_rcv_flush(bp); /* Clear device structure flags */ netif_stop_queue(dev); /* Deregister (free) IRQ */ free_irq(dev->irq, dev); return(0); } /* * ====================== * = dfx_int_pr_halt_id = * ====================== * * Overview: * Displays halt id's in string form. * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * Determine current halt id and display appropriate string. * * Return Codes: * None * * Assumptions: * None * * Side Effects: * None */ static void dfx_int_pr_halt_id(DFX_board_t *bp) { PI_UINT32 port_status; /* PDQ port status register value */ PI_UINT32 halt_id; /* PDQ port status halt ID */ /* Read the latest port status */ dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status); /* Display halt state transition information */ halt_id = (port_status & PI_PSTATUS_M_HALT_ID) >> PI_PSTATUS_V_HALT_ID; switch (halt_id) { case PI_HALT_ID_K_SELFTEST_TIMEOUT: printk("%s: Halt ID: Selftest Timeout\n", bp->dev->name); break; case PI_HALT_ID_K_PARITY_ERROR: printk("%s: Halt ID: Host Bus Parity Error\n", bp->dev->name); break; case PI_HALT_ID_K_HOST_DIR_HALT: printk("%s: Halt ID: Host-Directed Halt\n", bp->dev->name); break; case PI_HALT_ID_K_SW_FAULT: printk("%s: Halt ID: Adapter Software Fault\n", bp->dev->name); break; case PI_HALT_ID_K_HW_FAULT: printk("%s: Halt ID: Adapter Hardware Fault\n", bp->dev->name); break; case PI_HALT_ID_K_PC_TRACE: printk("%s: Halt ID: FDDI Network PC Trace Path Test\n", bp->dev->name); break; case PI_HALT_ID_K_DMA_ERROR: printk("%s: Halt ID: Adapter DMA Error\n", bp->dev->name); break; case PI_HALT_ID_K_IMAGE_CRC_ERROR: printk("%s: Halt ID: Firmware Image CRC Error\n", bp->dev->name); break; case PI_HALT_ID_K_BUS_EXCEPTION: printk("%s: Halt ID: 68000 Bus Exception\n", bp->dev->name); break; default: printk("%s: Halt ID: Unknown (code = %X)\n", bp->dev->name, halt_id); break; } } /* * ========================== * = dfx_int_type_0_process = * ========================== * * Overview: * Processes Type 0 interrupts. * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * Processes all enabled Type 0 interrupts. If the reason for the interrupt * is a serious fault on the adapter, then an error message is displayed * and the adapter is reset. * * One tricky potential timing window is the rapid succession of "link avail" * "link unavail" state change interrupts. The acknowledgement of the Type 0 * interrupt must be done before reading the state from the Port Status * register. This is true because a state change could occur after reading * the data, but before acknowledging the interrupt. If this state change * does happen, it would be lost because the driver is using the old state, * and it will never know about the new state because it subsequently * acknowledges the state change interrupt. * * INCORRECT CORRECT * read type 0 int reasons read type 0 int reasons * read adapter state ack type 0 interrupts * ack type 0 interrupts read adapter state * ... process interrupt ... ... process interrupt ... * * Return Codes: * None * * Assumptions: * None * * Side Effects: * An adapter reset may occur if the adapter has any Type 0 error interrupts * or if the port status indicates that the adapter is halted. The driver * is responsible for reinitializing the adapter with the current CAM * contents and adapter filter settings. */ static void dfx_int_type_0_process(DFX_board_t *bp) { PI_UINT32 type_0_status; /* Host Interrupt Type 0 register */ PI_UINT32 state; /* current adap state (from port status) */ /* * Read host interrupt Type 0 register to determine which Type 0 * interrupts are pending. Immediately write it back out to clear * those interrupts. */ dfx_port_read_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, &type_0_status); dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, type_0_status); /* Check for Type 0 error interrupts */ if (type_0_status & (PI_TYPE_0_STAT_M_NXM | PI_TYPE_0_STAT_M_PM_PAR_ERR | PI_TYPE_0_STAT_M_BUS_PAR_ERR)) { /* Check for Non-Existent Memory error */ if (type_0_status & PI_TYPE_0_STAT_M_NXM) printk("%s: Non-Existent Memory Access Error\n", bp->dev->name); /* Check for Packet Memory Parity error */ if (type_0_status & PI_TYPE_0_STAT_M_PM_PAR_ERR) printk("%s: Packet Memory Parity Error\n", bp->dev->name); /* Check for Host Bus Parity error */ if (type_0_status & PI_TYPE_0_STAT_M_BUS_PAR_ERR) printk("%s: Host Bus Parity Error\n", bp->dev->name); /* Reset adapter and bring it back on-line */ bp->link_available = PI_K_FALSE; /* link is no longer available */ bp->reset_type = 0; /* rerun on-board diagnostics */ printk("%s: Resetting adapter...\n", bp->dev->name); if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS) { printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name); dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); return; } printk("%s: Adapter reset successful!\n", bp->dev->name); return; } /* Check for transmit flush interrupt */ if (type_0_status & PI_TYPE_0_STAT_M_XMT_FLUSH) { /* Flush any pending xmt's and acknowledge the flush interrupt */ bp->link_available = PI_K_FALSE; /* link is no longer available */ dfx_xmt_flush(bp); /* flush any outstanding packets */ (void) dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_XMT_DATA_FLUSH_DONE, 0, 0, NULL); } /* Check for adapter state change */ if (type_0_status & PI_TYPE_0_STAT_M_STATE_CHANGE) { /* Get latest adapter state */ state = dfx_hw_adap_state_rd(bp); /* get adapter state */ if (state == PI_STATE_K_HALTED) { /* * Adapter has transitioned to HALTED state, try to reset * adapter to bring it back on-line. If reset fails, * leave the adapter in the broken state. */ printk("%s: Controller has transitioned to HALTED state!\n", bp->dev->name); dfx_int_pr_halt_id(bp); /* display halt id as string */ /* Reset adapter and bring it back on-line */ bp->link_available = PI_K_FALSE; /* link is no longer available */ bp->reset_type = 0; /* rerun on-board diagnostics */ printk("%s: Resetting adapter...\n", bp->dev->name); if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS) { printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name); dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); return; } printk("%s: Adapter reset successful!\n", bp->dev->name); } else if (state == PI_STATE_K_LINK_AVAIL) { bp->link_available = PI_K_TRUE; /* set link available flag */ } } } /* * ================== * = dfx_int_common = * ================== * * Overview: * Interrupt service routine (ISR) * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * This is the ISR which processes incoming adapter interrupts. * * Return Codes: * None * * Assumptions: * This routine assumes PDQ interrupts have not been disabled. * When interrupts are disabled at the PDQ, the Port Status register * is automatically cleared. This routine uses the Port Status * register value to determine whether a Type 0 interrupt occurred, * so it's important that adapter interrupts are not normally * enabled/disabled at the PDQ. * * It's vital that this routine is NOT reentered for the * same board and that the OS is not in another section of * code (eg. dfx_xmt_queue_pkt) for the same board on a * different thread. * * Side Effects: * Pending interrupts are serviced. Depending on the type of * interrupt, acknowledging and clearing the interrupt at the * PDQ involves writing a register to clear the interrupt bit * or updating completion indices. */ static void dfx_int_common(struct net_device *dev) { DFX_board_t *bp = dev->priv; PI_UINT32 port_status; /* Port Status register */ /* Process xmt interrupts - frequent case, so always call this routine */ if(dfx_xmt_done(bp)) /* free consumed xmt packets */ netif_wake_queue(dev); /* Process rcv interrupts - frequent case, so always call this routine */ dfx_rcv_queue_process(bp); /* service received LLC frames */ /* * Transmit and receive producer and completion indices are updated on the * adapter by writing to the Type 2 Producer register. Since the frequent * case is that we'll be processing either LLC transmit or receive buffers, * we'll optimize I/O writes by doing a single register write here. */ dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_2_PROD, bp->rcv_xmt_reg.lword); /* Read PDQ Port Status register to find out which interrupts need processing */ dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status); /* Process Type 0 interrupts (if any) - infrequent, so only call when needed */ if (port_status & PI_PSTATUS_M_TYPE_0_PENDING) dfx_int_type_0_process(bp); /* process Type 0 interrupts */ } /* * ================= * = dfx_interrupt = * ================= * * Overview: * Interrupt processing routine * * Returns: * Whether a valid interrupt was seen. * * Arguments: * irq - interrupt vector * dev_id - pointer to device information * regs - pointer to registers structure * * Functional Description: * This routine calls the interrupt processing routine for this adapter. It * disables and reenables adapter interrupts, as appropriate. We can support * shared interrupts since the incoming dev_id pointer provides our device * structure context. * * Return Codes: * IRQ_HANDLED - an IRQ was handled. * IRQ_NONE - no IRQ was handled. * * Assumptions: * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC * on Intel-based systems) is done by the operating system outside this * routine. * * System interrupts are enabled through this call. * * Side Effects: * Interrupts are disabled, then reenabled at the adapter. */ static irqreturn_t dfx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; DFX_board_t *bp; /* private board structure pointer */ /* Get board pointer only if device structure is valid */ bp = dev->priv; /* See if we're already servicing an interrupt */ /* Service adapter interrupts */ if (bp->bus_type == DFX_BUS_TYPE_PCI) { u32 status; dfx_port_read_long(bp, PFI_K_REG_STATUS, &status); if (!(status & PFI_STATUS_M_PDQ_INT)) return IRQ_NONE; spin_lock(&bp->lock); /* Disable PDQ-PFI interrupts at PFI */ dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, PFI_MODE_M_DMA_ENB); /* Call interrupt service routine for this adapter */ dfx_int_common(dev); /* Clear PDQ interrupt status bit and reenable interrupts */ dfx_port_write_long(bp, PFI_K_REG_STATUS, PFI_STATUS_M_PDQ_INT); dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, (PFI_MODE_M_PDQ_INT_ENB | PFI_MODE_M_DMA_ENB)); spin_unlock(&bp->lock); } else { u8 status; dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &status); if (!(status & PI_CONFIG_STAT_0_M_PEND)) return IRQ_NONE; spin_lock(&bp->lock); /* Disable interrupts at the ESIC */ status &= ~PI_CONFIG_STAT_0_M_INT_ENB; dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, status); /* Call interrupt service routine for this adapter */ dfx_int_common(dev); /* Reenable interrupts at the ESIC */ dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &status); status |= PI_CONFIG_STAT_0_M_INT_ENB; dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, status); spin_unlock(&bp->lock); } return IRQ_HANDLED; } /* * ===================== * = dfx_ctl_get_stats = * ===================== * * Overview: * Get statistics for FDDI adapter * * Returns: * Pointer to FDDI statistics structure * * Arguments: * dev - pointer to device information * * Functional Description: * Gets current MIB objects from adapter, then * returns FDDI statistics structure as defined * in if_fddi.h. * * Note: Since the FDDI statistics structure is * still new and the device structure doesn't * have an FDDI-specific get statistics handler, * we'll return the FDDI statistics structure as * a pointer to an Ethernet statistics structure. * That way, at least the first part of the statistics * structure can be decoded properly, and it allows * "smart" applications to perform a second cast to * decode the FDDI-specific statistics. * * We'll have to pay attention to this routine as the * device structure becomes more mature and LAN media * independent. * * Return Codes: * None * * Assumptions: * None * * Side Effects: * None */ static struct net_device_stats *dfx_ctl_get_stats(struct net_device *dev) { DFX_board_t *bp = dev->priv; /* Fill the bp->stats structure with driver-maintained counters */ bp->stats.gen.rx_packets = bp->rcv_total_frames; bp->stats.gen.tx_packets = bp->xmt_total_frames; bp->stats.gen.rx_bytes = bp->rcv_total_bytes; bp->stats.gen.tx_bytes = bp->xmt_total_bytes; bp->stats.gen.rx_errors = bp->rcv_crc_errors + bp->rcv_frame_status_errors + bp->rcv_length_errors; bp->stats.gen.tx_errors = bp->xmt_length_errors; bp->stats.gen.rx_dropped = bp->rcv_discards; bp->stats.gen.tx_dropped = bp->xmt_discards; bp->stats.gen.multicast = bp->rcv_multicast_frames; bp->stats.gen.collisions = 0; /* always zero (0) for FDDI */ /* Get FDDI SMT MIB objects */ bp->cmd_req_virt->cmd_type = PI_CMD_K_SMT_MIB_GET; if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) return((struct net_device_stats *) &bp->stats); /* Fill the bp->stats structure with the SMT MIB object values */ memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id)); bp->stats.smt_op_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id; bp->stats.smt_hi_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id; bp->stats.smt_lo_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id; memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data)); bp->stats.smt_mib_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id; bp->stats.smt_mac_cts = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct; bp->stats.smt_non_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct; bp->stats.smt_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct; bp->stats.smt_available_paths = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths; bp->stats.smt_config_capabilities = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities; bp->stats.smt_config_policy = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy; bp->stats.smt_connection_policy = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy; bp->stats.smt_t_notify = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify; bp->stats.smt_stat_rpt_policy = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy; bp->stats.smt_trace_max_expiration = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration; bp->stats.smt_bypass_present = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present; bp->stats.smt_ecm_state = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state; bp->stats.smt_cf_state = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state; bp->stats.smt_remote_disconnect_flag = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag; bp->stats.smt_station_status = bp->cmd_rsp_virt->smt_mib_get.smt_station_status; bp->stats.smt_peer_wrap_flag = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag; bp->stats.smt_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls; bp->stats.smt_transition_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls; bp->stats.mac_frame_status_functions = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions; bp->stats.mac_t_max_capability = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability; bp->stats.mac_tvx_capability = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability; bp->stats.mac_available_paths = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths; bp->stats.mac_current_path = bp->cmd_rsp_virt->smt_mib_get.mac_current_path; memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN); memcpy(bp->stats.mac_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_downstream_nbr, FDDI_K_ALEN); memcpy(bp->stats.mac_old_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_upstream_nbr, FDDI_K_ALEN); memcpy(bp->stats.mac_old_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_downstream_nbr, FDDI_K_ALEN); bp->stats.mac_dup_address_test = bp->cmd_rsp_virt->smt_mib_get.mac_dup_address_test; bp->stats.mac_requested_paths = bp->cmd_rsp_virt->smt_mib_get.mac_requested_paths; bp->stats.mac_downstream_port_type = bp->cmd_rsp_virt->smt_mib_get.mac_downstream_port_type; memcpy(bp->stats.mac_smt_address, &bp->cmd_rsp_virt->smt_mib_get.mac_smt_address, FDDI_K_ALEN); bp->stats.mac_t_req = bp->cmd_rsp_virt->smt_mib_get.mac_t_req; bp->stats.mac_t_neg = bp->cmd_rsp_virt->smt_mib_get.mac_t_neg; bp->stats.mac_t_max = bp->cmd_rsp_virt->smt_mib_get.mac_t_max; bp->stats.mac_tvx_value = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_value; bp->stats.mac_frame_error_threshold = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_threshold; bp->stats.mac_frame_error_ratio = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_ratio; bp->stats.mac_rmt_state = bp->cmd_rsp_virt->smt_mib_get.mac_rmt_state; bp->stats.mac_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_da_flag; bp->stats.mac_una_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_unda_flag; bp->stats.mac_frame_error_flag = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_flag; bp->stats.mac_ma_unitdata_available = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_available; bp->stats.mac_hardware_present = bp->cmd_rsp_virt->smt_mib_get.mac_hardware_present; bp->stats.mac_ma_unitdata_enable = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_enable; bp->stats.path_tvx_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_tvx_lower_bound; bp->stats.path_t_max_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_t_max_lower_bound; bp->stats.path_max_t_req = bp->cmd_rsp_virt->smt_mib_get.path_max_t_req; memcpy(bp->stats.path_configuration, &bp->cmd_rsp_virt->smt_mib_get.path_configuration, sizeof(bp->cmd_rsp_virt->smt_mib_get.path_configuration)); bp->stats.port_my_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[0]; bp->stats.port_my_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[1]; bp->stats.port_neighbor_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[0]; bp->stats.port_neighbor_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[1]; bp->stats.port_connection_policies[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[0]; bp->stats.port_connection_policies[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[1]; bp->stats.port_mac_indicated[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[0]; bp->stats.port_mac_indicated[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[1]; bp->stats.port_current_path[0] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[0]; bp->stats.port_current_path[1] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[1]; memcpy(&bp->stats.port_requested_paths[0*3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[0], 3); memcpy(&bp->stats.port_requested_paths[1*3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[1], 3); bp->stats.port_mac_placement[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[0]; bp->stats.port_mac_placement[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[1]; bp->stats.port_available_paths[0] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[0]; bp->stats.port_available_paths[1] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[1]; bp->stats.port_pmd_class[0] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[0]; bp->stats.port_pmd_class[1] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[1]; bp->stats.port_connection_capabilities[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[0]; bp->stats.port_connection_capabilities[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[1]; bp->stats.port_bs_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[0]; bp->stats.port_bs_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[1]; bp->stats.port_ler_estimate[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[0]; bp->stats.port_ler_estimate[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[1]; bp->stats.port_ler_cutoff[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[0]; bp->stats.port_ler_cutoff[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[1]; bp->stats.port_ler_alarm[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[0]; bp->stats.port_ler_alarm[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[1]; bp->stats.port_connect_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[0]; bp->stats.port_connect_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[1]; bp->stats.port_pcm_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[0]; bp->stats.port_pcm_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[1]; bp->stats.port_pc_withhold[0] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[0]; bp->stats.port_pc_withhold[1] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[1]; bp->stats.port_ler_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[0]; bp->stats.port_ler_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[1]; bp->stats.port_hardware_present[0] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[0]; bp->stats.port_hardware_present[1] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_pres