diff options
Diffstat (limited to 'drivers/net/fddi/defxx.c')
-rw-r--r-- | drivers/net/fddi/defxx.c | 3739 |
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 |