summaryrefslogtreecommitdiff
path: root/drivers/net/fddi/defxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/fddi/defxx.c')
-rw-r--r--drivers/net/fddi/defxx.c3739
1 files changed, 3739 insertions, 0 deletions
diff --git a/drivers/net/fddi/defxx.c b/drivers/net/fddi/defxx.c
new file mode 100644
index 000000000000..4ad80f771099
--- /dev/null
+++ b/drivers/net/fddi/defxx.c
@@ -0,0 +1,3739 @@
+/*
+ * 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 TURBOchannel, EISA and PCI controller families. Supported
+ * adapters include:
+ *
+ * DEC FDDIcontroller/TURBOchannel (DEFTA)
+ * DEC FDDIcontroller/EISA (DEFEA)
+ * DEC FDDIcontroller/PCI (DEFPA)
+ *
+ * The original author:
+ * LVS Lawrence V. Stefani <lstefani@yahoo.com>
+ *
+ * Maintainers:
+ * macro Maciej W. Rozycki <macro@linux-mips.org>
+ *
+ * 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.
+ * 23 Oct 2006 macro Big-endian host support.
+ * 14 Dec 2006 macro TURBOchannel support.
+ */
+
+/* Include files */
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/eisa.h>
+#include <linux/errno.h>
+#include <linux/fddidevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tc.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include "defxx.h"
+
+/* Version information string should be updated prior to each new release! */
+#define DRV_NAME "defxx"
+#define DRV_VERSION "v1.10"
+#define DRV_RELDATE "2006/12/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)
+
+#ifdef CONFIG_PCI
+#define DFX_BUS_PCI(dev) (dev->bus == &pci_bus_type)
+#else
+#define DFX_BUS_PCI(dev) 0
+#endif
+
+#ifdef CONFIG_EISA
+#define DFX_BUS_EISA(dev) (dev->bus == &eisa_bus_type)
+#else
+#define DFX_BUS_EISA(dev) 0
+#endif
+
+#ifdef CONFIG_TC
+#define DFX_BUS_TC(dev) (dev->bus == &tc_bus_type)
+#else
+#define DFX_BUS_TC(dev) 0
+#endif
+
+#ifdef CONFIG_DEFXX_MMIO
+#define DFX_MMIO 1
+#else
+#define DFX_MMIO 0
+#endif
+
+/* Define module-wide (static) routines */
+
+static void dfx_bus_init(struct net_device *dev);
+static void dfx_bus_uninit(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,
+ resource_size_t bar_start);
+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);
+
+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 netdev_tx_t 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 pci_driver dfx_pci_driver;
+static struct eisa_driver dfx_eisa_driver;
+static struct tc_driver dfx_tc_driver;
+
+
+/*
+ * =======================
+ * = 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_long, this is a value to write;
+ * for dfx_port_read_long, 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 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_writel(DFX_board_t *bp, int offset, u32 data)
+{
+ writel(data, bp->base.mem + offset);
+ mb();
+}
+
+static inline void dfx_outl(DFX_board_t *bp, int offset, u32 data)
+{
+ outl(data, bp->base.port + offset);
+}
+
+static void dfx_port_write_long(DFX_board_t *bp, int offset, u32 data)
+{
+ struct device __maybe_unused *bdev = bp->bus_dev;
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+
+ if (dfx_use_mmio)
+ dfx_writel(bp, offset, data);
+ else
+ dfx_outl(bp, offset, data);
+}
+
+
+static inline void dfx_readl(DFX_board_t *bp, int offset, u32 *data)
+{
+ mb();
+ *data = readl(bp->base.mem + offset);
+}
+
+static inline void dfx_inl(DFX_board_t *bp, int offset, u32 *data)
+{
+ *data = inl(bp->base.port + offset);
+}
+
+static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data)
+{
+ struct device __maybe_unused *bdev = bp->bus_dev;
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+
+ if (dfx_use_mmio)
+ dfx_readl(bp, offset, data);
+ else
+ dfx_inl(bp, offset, data);
+}
+
+
+/*
+ * ================
+ * = dfx_get_bars =
+ * ================
+ *
+ * Overview:
+ * Retrieves the address range used to access control and status
+ * registers.
+ *
+ * Returns:
+ * None
+ *
+ * Arguments:
+ * bdev - pointer to device information
+ * bar_start - pointer to store the start address
+ * bar_len - pointer to store the length of the area
+ *
+ * Assumptions:
+ * I am sure there are some.
+ *
+ * Side Effects:
+ * None
+ */
+static void dfx_get_bars(struct device *bdev,
+ resource_size_t *bar_start, resource_size_t *bar_len)
+{
+ int dfx_bus_pci = DFX_BUS_PCI(bdev);
+ int dfx_bus_eisa = DFX_BUS_EISA(bdev);
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+
+ if (dfx_bus_pci) {
+ int num = dfx_use_mmio ? 0 : 1;
+
+ *bar_start = pci_resource_start(to_pci_dev(bdev), num);
+ *bar_len = pci_resource_len(to_pci_dev(bdev), num);
+ }
+ if (dfx_bus_eisa) {
+ unsigned long base_addr = to_eisa_device(bdev)->base_addr;
+ resource_size_t bar;
+
+ if (dfx_use_mmio) {
+ bar = inb(base_addr + PI_ESIC_K_MEM_ADD_CMP_2);
+ bar <<= 8;
+ bar |= inb(base_addr + PI_ESIC_K_MEM_ADD_CMP_1);
+ bar <<= 8;
+ bar |= inb(base_addr + PI_ESIC_K_MEM_ADD_CMP_0);
+ bar <<= 16;
+ *bar_start = bar;
+ bar = inb(base_addr + PI_ESIC_K_MEM_ADD_MASK_2);
+ bar <<= 8;
+ bar |= inb(base_addr + PI_ESIC_K_MEM_ADD_MASK_1);
+ bar <<= 8;
+ bar |= inb(base_addr + PI_ESIC_K_MEM_ADD_MASK_0);
+ bar <<= 16;
+ *bar_len = (bar | PI_MEM_ADD_MASK_M) + 1;
+ } else {
+ *bar_start = base_addr;
+ *bar_len = PI_ESIC_K_CSR_IO_LEN;
+ }
+ }
+ if (dfx_bus_tc) {
+ *bar_start = to_tc_dev(bdev)->resource.start +
+ PI_TC_K_CSR_OFFSET;
+ *bar_len = PI_TC_K_CSR_LEN;
+ }
+}
+
+static const struct net_device_ops dfx_netdev_ops = {
+ .ndo_open = dfx_open,
+ .ndo_stop = dfx_close,
+ .ndo_start_xmit = dfx_xmt_queue_pkt,
+ .ndo_get_stats = dfx_ctl_get_stats,
+ .ndo_set_rx_mode = dfx_ctl_set_multicast_list,
+ .ndo_set_mac_address = dfx_ctl_set_mac_address,
+};
+
+/*
+ * ================
+ * = dfx_register =
+ * ================
+ *
+ * Overview:
+ * Initializes a supported FDDI controller
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * bdev - pointer to device information
+ *
+ * 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_register(struct device *bdev)
+{
+ static int version_disp;
+ int dfx_bus_pci = DFX_BUS_PCI(bdev);
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+ const char *print_name = dev_name(bdev);
+ struct net_device *dev;
+ DFX_board_t *bp; /* board pointer */
+ resource_size_t bar_start = 0; /* pointer to port */
+ resource_size_t bar_len = 0; /* resource length */
+ int alloc_size; /* total buffer size used */
+ struct resource *region;
+ int err = 0;
+
+ 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 */
+ }
+
+ 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 (dfx_bus_pci && pci_enable_device(to_pci_dev(bdev))) {
+ printk(KERN_ERR "%s: Cannot enable PCI device, aborting\n",
+ print_name);
+ goto err_out;
+ }
+
+ SET_NETDEV_DEV(dev, bdev);
+
+ bp = netdev_priv(dev);
+ bp->bus_dev = bdev;
+ dev_set_drvdata(bdev, dev);
+
+ dfx_get_bars(bdev, &bar_start, &bar_len);
+
+ if (dfx_use_mmio)
+ region = request_mem_region(bar_start, bar_len, print_name);
+ else
+ region = request_region(bar_start, bar_len, print_name);
+ if (!region) {
+ printk(KERN_ERR "%s: Cannot reserve I/O resource "
+ "0x%lx @ 0x%lx, aborting\n",
+ print_name, (long)bar_len, (long)bar_start);
+ err = -EBUSY;
+ goto err_out_disable;
+ }
+
+ /* Set up I/O base address. */
+ if (dfx_use_mmio) {
+ bp->base.mem = ioremap_nocache(bar_start, bar_len);
+ if (!bp->base.mem) {
+ printk(KERN_ERR "%s: Cannot map MMIO\n", print_name);
+ err = -ENOMEM;
+ goto err_out_region;
+ }
+ } else {
+ bp->base.port = bar_start;
+ dev->base_addr = bar_start;
+ }
+
+ /* Initialize new device structure */
+ dev->netdev_ops = &dfx_netdev_ops;
+
+ if (dfx_bus_pci)
+ pci_set_master(to_pci_dev(bdev));
+
+ if (dfx_driver_init(dev, print_name, bar_start) != DFX_K_SUCCESS) {
+ err = -ENODEV;
+ goto err_out_unmap;
+ }
+
+ 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)
+ dma_free_coherent(bdev, alloc_size,
+ bp->kmalloced, bp->kmalloced_dma);
+
+err_out_unmap:
+ if (dfx_use_mmio)
+ iounmap(bp->base.mem);
+
+err_out_region:
+ if (dfx_use_mmio)
+ release_mem_region(bar_start, bar_len);
+ else
+ release_region(bar_start, bar_len);
+
+err_out_disable:
+ if (dfx_bus_pci)
+ pci_disable_device(to_pci_dev(bdev));
+
+err_out:
+ free_netdev(dev);
+ return err;
+}
+
+
+/*
+ * ================
+ * = dfx_bus_init =
+ * ================
+ *
+ * Overview:
+ * Initializes the bus-specific controller 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:
+ * bp->base 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 = netdev_priv(dev);
+ struct device *bdev = bp->bus_dev;
+ int dfx_bus_pci = DFX_BUS_PCI(bdev);
+ int dfx_bus_eisa = DFX_BUS_EISA(bdev);
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+ u8 val;
+
+ DBG_printk("In dfx_bus_init...\n");
+
+ /* Initialize a pointer back to the net_device struct */
+ bp->dev = dev;
+
+ /* Initialize adapter based on bus type */
+
+ if (dfx_bus_tc)
+ dev->irq = to_tc_dev(bdev)->interrupt;
+ if (dfx_bus_eisa) {
+ unsigned long base_addr = to_eisa_device(bdev)->base_addr;
+
+ /* Get the interrupt level from the ESIC chip. */
+ val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
+ val &= PI_CONFIG_STAT_0_M_IRQ;
+ val >>= PI_CONFIG_STAT_0_V_IRQ;
+
+ switch (val) {
+ 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 memory decoding (MEMCS0) and/or port decoding
+ * (IOCS1/IOCS0) as appropriate in Function Control
+ * Register. One of the port chip selects seems to be
+ * used for the Burst Holdoff register, but this bit of
+ * documentation is missing and as yet it has not been
+ * determined which of the two. This is also the reason
+ * the size of the decoded port range is twice as large
+ * as one required by the PDQ.
+ */
+
+ /* Set the decode range of the board. */
+ val = ((bp->base.port >> 12) << PI_IO_CMP_V_SLOT);
+ outb(base_addr + PI_ESIC_K_IO_ADD_CMP_0_1, val);
+ outb(base_addr + PI_ESIC_K_IO_ADD_CMP_0_0, 0);
+ outb(base_addr + PI_ESIC_K_IO_ADD_CMP_1_1, val);
+ outb(base_addr + PI_ESIC_K_IO_ADD_CMP_1_0, 0);
+ val = PI_ESIC_K_CSR_IO_LEN - 1;
+ outb(base_addr + PI_ESIC_K_IO_ADD_MASK_0_1, (val >> 8) & 0xff);
+ outb(base_addr + PI_ESIC_K_IO_ADD_MASK_0_0, val & 0xff);
+ outb(base_addr + PI_ESIC_K_IO_ADD_MASK_1_1, (val >> 8) & 0xff);
+ outb(base_addr + PI_ESIC_K_IO_ADD_MASK_1_0, val & 0xff);
+
+ /* Enable the decoders. */
+ val = PI_FUNCTION_CNTRL_M_IOCS1 | PI_FUNCTION_CNTRL_M_IOCS0;
+ if (dfx_use_mmio)
+ val |= PI_FUNCTION_CNTRL_M_MEMCS0;
+ outb(base_addr + PI_ESIC_K_FUNCTION_CNTRL, val);
+
+ /*
+ * Enable access to the rest of the module
+ * (including PDQ and packet memory).
+ */
+ val = PI_SLOT_CNTRL_M_ENB;
+ outb(base_addr + PI_ESIC_K_SLOT_CNTRL, val);
+
+ /*
+ * Map PDQ registers into memory or port space. This is
+ * done with a bit in the Burst Holdoff register.
+ */
+ val = inb(base_addr + PI_DEFEA_K_BURST_HOLDOFF);
+ if (dfx_use_mmio)
+ val |= PI_BURST_HOLDOFF_V_MEM_MAP;
+ else
+ val &= ~PI_BURST_HOLDOFF_V_MEM_MAP;
+ outb(base_addr + PI_DEFEA_K_BURST_HOLDOFF, val);
+
+ /* Enable interrupts at EISA bus interface chip (ESIC) */
+ val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
+ val |= PI_CONFIG_STAT_0_M_INT_ENB;
+ outb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0, val);
+ }
+ if (dfx_bus_pci) {
+ struct pci_dev *pdev = to_pci_dev(bdev);
+
+ /* 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) {
+ val = PFI_K_LAT_TIMER_DEF;
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, val);
+ }
+
+ /* Enable interrupts at PCI bus interface chip (PFI) */
+ val = PFI_MODE_M_PDQ_INT_ENB | PFI_MODE_M_DMA_ENB;
+ dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, val);
+ }
+}
+
+/*
+ * ==================
+ * = dfx_bus_uninit =
+ * ==================
+ *
+ * Overview:
+ * Uninitializes the bus-specific controller logic.
+ *
+ * Returns:
+ * None
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * Perform bus-specific logic uninitialization.
+ *
+ * Return Codes:
+ * None
+ *
+ * Assumptions:
+ * bp->base has already been set with the proper
+ * base I/O address for this device.
+ *
+ * Side Effects:
+ * Interrupts are disabled at the adapter bus-specific logic.
+ */
+
+static void __devexit dfx_bus_uninit(struct net_device *dev)
+{
+ DFX_board_t *bp = netdev_priv(dev);
+ struct device *bdev = bp->bus_dev;
+ int dfx_bus_pci = DFX_BUS_PCI(bdev);
+ int dfx_bus_eisa = DFX_BUS_EISA(bdev);
+ u8 val;
+
+ DBG_printk("In dfx_bus_uninit...\n");
+
+ /* Uninitialize adapter based on bus type */
+
+ if (dfx_bus_eisa) {
+ unsigned long base_addr = to_eisa_device(bdev)->base_addr;
+
+ /* Disable interrupts at EISA bus interface chip (ESIC) */
+ val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
+ val &= ~PI_CONFIG_STAT_0_M_INT_ENB;
+ outb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0, val);
+ }
+ if (dfx_bus_pci) {
+ /* Disable interrupts at PCI bus interface chip (PFI) */
+ dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, 0);
+ }
+}
+
+
+/*
+ * ========================
+ * = 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)
+{
+ struct device __maybe_unused *bdev = bp->bus_dev;
+ int dfx_bus_eisa = DFX_BUS_EISA(bdev);
+ int status; /* return code from adapter port control call */
+ 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 (dfx_bus_eisa) {
+ /*
+ * 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 (to_eisa_device(bdev)->id.driver_data == 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,
+ resource_size_t bar_start)
+{
+ DFX_board_t *bp = netdev_priv(dev);
+ struct device *bdev = bp->bus_dev;
+ int dfx_bus_pci = DFX_BUS_PCI(bdev);
+ int dfx_bus_eisa = DFX_BUS_EISA(bdev);
+ int dfx_bus_tc = DFX_BUS_TC(bdev);
+ int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
+ 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 */
+ __le32 le32;
+ char *board_name = NULL;
+
+ 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;
+ }
+ le32 = cpu_to_le32(data);
+ memcpy(&bp->factory_mac_addr[0], &le32, 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;
+ }
+ le32 = cpu_to_le32(data);
+ memcpy(&bp->factory_mac_addr[4], &le32, 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 (dfx_bus_tc)
+ board_name = "DEFTA";
+ if (dfx_bus_eisa)
+ board_name = "DEFEA";
+ if (dfx_bus_pci)
+ board_name = "DEFPA";
+ pr_info("%s: %s at %saddr = 0x%llx, IRQ = %d, Hardware addr = %pMF\n",
+ print_name, board_name, dfx_use_mmio ? "" : "I/O ",
+ (long long)bar_start, dev->irq, dev->dev_addr);
+
+ /*
+ * 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