diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/defxx.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/defxx.c')
-rw-r--r-- | drivers/net/defxx.c | 3463 |
1 files changed, 3463 insertions, 0 deletions
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c new file mode 100644 index 00000000000..a6aa56598f2 --- /dev/null +++ b/drivers/net/defxx.c @@ -0,0 +1,3463 @@ +/* + * 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 <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. + */ + +/* Include files */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/fddidevice.h> +#include <linux/skbuff.h> +#include <linux/bitops.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.07" +#define DRV_RELDATE "2004/08/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 void 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); + 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 regis |