/* * 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@orcam.me.uk> * * 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. * 01 Jul 2014 macro Fixes for DMA on 64-bit hosts. * 10 Mar 2021 macro Dynamic MMIO vs port I/O.
*/
/* Version information string should be updated prior to each new release! */ #define DRV_NAME "defxx" #define DRV_VERSION "v1.12" #define DRV_RELDATE "2021/03/10"
staticconstchar version[] =
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)
/* * ======================= * = 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.
*/
if (dfx_use_mmio)
dfx_readl(bp, offset, data); else
dfx_inl(bp, offset, data);
}
/* * ================ * = dfx_get_bars = * ================ * * Overview: * Retrieves the address ranges used to access control and status * registers. * * Returns: * None * * Arguments: * bp - pointer to board information * bar_start - pointer to store the start addresses * bar_len - pointer to store the lengths of the areas * * Assumptions: * I am sure there are some. * * Side Effects: * None
*/ staticvoid dfx_get_bars(DFX_board_t *bp,
resource_size_t *bar_start, resource_size_t *bar_len)
{ struct device *bdev = bp->bus_dev; int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev);
if (dfx_bus_pci) { int num = dfx_use_mmio ? 0 : 1;
/* * ================ * = 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.
*/ staticint dfx_register(struct device *bdev)
{ staticint version_disp; int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev); constchar *print_name = dev_name(bdev); struct net_device *dev;
DFX_board_t *bp; /* board pointer */
resource_size_t bar_start[3] = {0}; /* pointers to ports */
resource_size_t bar_len[3] = {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;
}
err_out_unmap: if (dfx_use_mmio)
iounmap(bp->base.mem);
err_out_esic_region: if (bar_start[2] != 0)
release_region(bar_start[2], bar_len[2]);
err_out_bh_region: if (bar_start[1] != 0)
release_region(bar_start[1], bar_len[1]);
err_out_csr_region: if (dfx_use_mmio)
release_mem_region(bar_start[0], bar_len[0]); else
release_region(bar_start[0], bar_len[0]);
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.
*/
staticvoid dfx_bus_init(struct net_device *dev)
{
DFX_board_t *bp = netdev_priv(dev); struct device *bdev = bp->bus_dev; int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev);
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) { unsignedlong base_addr = to_eisa_device(bdev)->base_addr;
/* Disable the board before fiddling with the decoders. */
outb(0, base_addr + PI_ESIC_K_SLOT_CNTRL);
/* 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 (MEMCS1) and/or port decoding * (IOCS1/IOCS0) as appropriate in Function Control * Register. MEMCS1 or IOCS0 is used for PDQ registers, * taking 16 32-bit words, while IOCS1 is used for the * Burst Holdoff register, taking a single 32-bit word * only. We use the slot-specific I/O range as per the * ESIC spec, that is set bits 15:12 in the mask registers * to mask them out.
*/
/* Set the decode range of the board. */
val = 0;
outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_0_1);
val = PI_DEFEA_K_CSR_IO;
outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_0_0);
val = PI_IO_CMP_M_SLOT;
outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_0_1);
val = (PI_ESIC_K_CSR_IO_LEN - 1) & ~3;
outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_0_0);
val = 0;
outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_1_1);
val = PI_DEFEA_K_BURST_HOLDOFF;
outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_1_0);
val = PI_IO_CMP_M_SLOT;
outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_1_1);
val = (PI_ESIC_K_BURST_HOLDOFF_LEN - 1) & ~3;
outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_1_0);
/* Enable the decoders. */
val = PI_FUNCTION_CNTRL_M_IOCS1; if (dfx_use_mmio)
val |= PI_FUNCTION_CNTRL_M_MEMCS1; else
val |= PI_FUNCTION_CNTRL_M_IOCS0;
outb(val, base_addr + PI_ESIC_K_FUNCTION_CNTRL);
/* * Enable access to the rest of the module * (including PDQ and packet memory).
*/
val = PI_SLOT_CNTRL_M_ENB;
outb(val, base_addr + PI_ESIC_K_SLOT_CNTRL);
/* * 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_M_MEM_MAP; else
val &= ~PI_BURST_HOLDOFF_M_MEM_MAP;
outb(val, base_addr + PI_DEFEA_K_BURST_HOLDOFF);
/* 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(val, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
} 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.
*/
if (dfx_bus_eisa) { unsignedlong 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(val, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
/* Disable the board. */
outb(0, base_addr + PI_ESIC_K_SLOT_CNTRL);
/* Disable memory and port decoders. */
outb(0, base_addr + PI_ESIC_K_FUNCTION_CNTRL);
} 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
*/
staticvoid 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 dma_alloc_coherent() call is physically * contiguous, locked memory. * * Side Effects: * Adapter is reset and should be in DMA_UNAVAILABLE state before * returning from this routine.
*/
staticint dfx_driver_init(struct net_device *dev, constchar *print_name,
resource_size_t bar_start)
{
DFX_board_t *bp = netdev_priv(dev); struct device *bdev = bp->bus_dev; int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev); 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.
*/
/* * 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.
*/
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.
*/
/* * ================= * = 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.
*/
staticint dfx_adap_init(DFX_board_t *bp, int get_buffers)
{
DBG_printk("In dfx_adap_init...\n");
/* 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.
*/
/* * Clear Type 1 and Type 2 registers before going to DMA_AVAILABLE state * * Note: We only need to clear host copies of these registers. The PDQ reset * takes care of the on-board register values.
*/
if (dfx_hw_port_ctrl_req(bp,
PI_PCTRL_M_SUB_CMD,
PI_SUB_CMD_K_BURST_SIZE_SET,
bp->burst_size,
NULL) != DFX_K_SUCCESS)
{
printk("%s: Could not set adapter burst size!\n", bp->dev->name); return DFX_K_FAILURE;
}
/* * Set base address of Consumer Block * * Assumption: 32-bit physical address of consumer block is 64 byte * aligned. That is, bits 0-5 of the address must be zero.
*/
if (dfx_hw_port_ctrl_req(bp,
PI_PCTRL_M_CONS_BLOCK,
bp->cons_block_phys,
0,
NULL) != DFX_K_SUCCESS)
{
printk("%s: Could not set consumer block address!\n", bp->dev->name); return DFX_K_FAILURE;
}
/* * Set the base address of Descriptor Block and bring adapter * to DMA_AVAILABLE state. * * Note: We also set the literal and data swapping requirements * in this command. * * Assumption: 32-bit physical address of descriptor block * is 8Kbyte aligned.
*/ if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_INIT,
(u32)(bp->descr_block_phys |
PI_PDATA_A_INIT_M_BSWAP_INIT),
0, NULL) != DFX_K_SUCCESS) {
printk("%s: Could not set descriptor block address!\n",
bp->dev->name); return DFX_K_FAILURE;
}
/* * ============ * = dfx_open = * ============ * * Overview: * Opens the adapter * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This function brings the adapter to an operational state. * * Return Codes: * 0 - Adapter was successfully opened * -EAGAIN - Could not register IRQ or adapter initialization failed * * Assumptions: * This routine should only be called for a device that was * initialized successfully. * * Side Effects: * Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state * if the open is successful.
*/
/* Register IRQ - support shared interrupts by passing device ptr */
ret = request_irq(dev->irq, dfx_interrupt, IRQF_SHARED, dev->name,
dev); if (ret) {
printk(KERN_ERR "%s: Requested IRQ %d is busy\n", dev->name, dev->irq); return ret;
}
/* * Set current address to factory MAC address * * Note: We've already done this step in dfx_driver_init. * However, it's possible that a user has set a node * address override, then closed and reopened the * adapter. Unless we reset the device address field * now, we'll continue to use the existing modified * address.
*/
dev_addr_set(dev, bp->factory_mac_addr);
/* Clear local unicast/multicast address tables and counts */
/* Set device structure info */
netif_start_queue(dev); return 0;
}
/* * ============= * = dfx_close = * ============= * * Overview: * Closes the device/module. * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This routine closes the adapter and brings it to a safe state. * The interrupt service routine is deregistered with the OS. * The adapter can be opened again with another call to dfx_open(). * * Return Codes: * Always return 0. * * Assumptions: * No further requests for this adapter are made after this routine is * called. dfx_open() can be called to reset and reinitialize the * adapter. * * Side Effects: * Adapter should be in DMA_UNAVAILABLE state upon completion of this * routine.
*/
/* * Flush any pending transmit buffers * * Note: It's important that we flush the transmit buffers * BEFORE we clear our copy of the Type 2 register. * Otherwise, we'll have no idea how many buffers * we need to free.
*/
dfx_xmt_flush(bp);
/* * Clear Type 1 and Type 2 registers after adapter reset * * Note: Even though we're closing the adapter, it's * possible that an interrupt will occur after * dfx_close is called. Without some assurance to * the contrary we want to make sure that we don't * process receive and transmit LLC frames and update * the Type 2 register with bad information.
*/
/* Release all dynamically allocate skb in the receive ring. */
dfx_rcv_flush(bp);
/* Clear device structure flags */
netif_stop_queue(dev);
/* Deregister (free) IRQ */
free_irq(dev->irq, dev);
return 0;
}
/* * ====================== * = dfx_int_pr_halt_id = * ====================== * * Overview: * Displays halt id's in string form. * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * Determine current halt id and display appropriate string. * * Return Codes: * None * * Assumptions: * None * * Side Effects: * None
*/
staticvoid dfx_int_pr_halt_id(DFX_board_t *bp)
{
PI_UINT32 port_status; /* PDQ port status register value */
PI_UINT32 halt_id; /* PDQ port status halt ID */
/* * ========================== * = dfx_int_type_0_process = * ========================== * * Overview: * Processes Type 0 interrupts. * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * Processes all enabled Type 0 interrupts. If the reason for the interrupt * is a serious fault on the adapter, then an error message is displayed * and the adapter is reset. * * One tricky potential timing window is the rapid succession of "link avail" * "link unavail" state change interrupts. The acknowledgement of the Type 0 * interrupt must be done before reading the state from the Port Status * register. This is true because a state change could occur after reading * the data, but before acknowledging the interrupt. If this state change * does happen, it would be lost because the driver is using the old state, * and it will never know about the new state because it subsequently * acknowledges the state change interrupt. * * INCORRECT CORRECT * read type 0 int reasons read type 0 int reasons * read adapter state ack type 0 interrupts * ack type 0 interrupts read adapter state * ... process interrupt ... ... process interrupt ... * * Return Codes: * None * * Assumptions: * None * * Side Effects: * An adapter reset may occur if the adapter has any Type 0 error interrupts * or if the port status indicates that the adapter is halted. The driver * is responsible for reinitializing the adapter with the current CAM * contents and adapter filter settings.
*/
{
PI_UINT32 type_0_status; /* Host Interrupt Type 0 register */
PI_UINT32 state; /* current adap state (from port status) */
/* * Read host interrupt Type 0 register to determine which Type 0 * interrupts are pending. Immediately write it back out to clear * those interrupts.
*/
if (type_0_status & (PI_TYPE_0_STAT_M_NXM |
PI_TYPE_0_STAT_M_PM_PAR_ERR |
PI_TYPE_0_STAT_M_BUS_PAR_ERR))
{ /* Check for Non-Existent Memory error */
if (type_0_status & PI_TYPE_0_STAT_M_NXM)
printk("%s: Non-Existent Memory Access Error\n", bp->dev->name);
/* Check for Packet Memory Parity error */
if (type_0_status & PI_TYPE_0_STAT_M_PM_PAR_ERR)
printk("%s: Packet Memory Parity Error\n", bp->dev->name);
/* Check for Host Bus Parity error */
if (type_0_status & PI_TYPE_0_STAT_M_BUS_PAR_ERR)
printk("%s: Host Bus Parity Error\n", bp->dev->name);
/* Reset adapter and bring it back on-line */
bp->link_available = PI_K_FALSE; /* link is no longer available */
bp->reset_type = 0; /* rerun on-board diagnostics */
printk("%s: Resetting adapter...\n", bp->dev->name); if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS)
{
printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name);
dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); return;
}
printk("%s: Adapter reset successful!\n", bp->dev->name); return;
}
/* Check for transmit flush interrupt */
if (type_0_status & PI_TYPE_0_STAT_M_XMT_FLUSH)
{ /* Flush any pending xmt's and acknowledge the flush interrupt */
bp->link_available = PI_K_FALSE; /* link is no longer available */
dfx_xmt_flush(bp); /* flush any outstanding packets */
(void) dfx_hw_port_ctrl_req(bp,
PI_PCTRL_M_XMT_DATA_FLUSH_DONE,
0,
0,
NULL);
}
/* Check for adapter state change */
if (type_0_status & PI_TYPE_0_STAT_M_STATE_CHANGE)
{ /* Get latest adapter state */
state = dfx_hw_adap_state_rd(bp); /* get adapter state */ if (state == PI_STATE_K_HALTED)
{ /* * Adapter has transitioned to HALTED state, try to reset * adapter to bring it back on-line. If reset fails, * leave the adapter in the broken state.
*/
printk("%s: Controller has transitioned to HALTED state!\n", bp->dev->name);
dfx_int_pr_halt_id(bp); /* display halt id as string */
/* Reset adapter and bring it back on-line */
bp->link_available = PI_K_FALSE; /* link is no longer available */
bp->reset_type = 0; /* rerun on-board diagnostics */
printk("%s: Resetting adapter...\n", bp->dev->name); if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS)
{
printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name);
dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); return;
}
printk("%s: Adapter reset successful!\n", bp->dev->name);
} elseif (state == PI_STATE_K_LINK_AVAIL)
{
bp->link_available = PI_K_TRUE; /* set link available flag */
}
}
}
/* * ================== * = dfx_int_common = * ================== * * Overview: * Interrupt service routine (ISR) * * Returns: * None * * Arguments: * bp - pointer to board information * * Functional Description: * This is the ISR which processes incoming adapter interrupts. * * Return Codes: * None * * Assumptions: * This routine assumes PDQ interrupts have not been disabled. * When interrupts are disabled at the PDQ, the Port Status register * is automatically cleared. This routine uses the Port Status * register value to determine whether a Type 0 interrupt occurred, * so it's important that adapter interrupts are not normally * enabled/disabled at the PDQ. * * It's vital that this routine is NOT reentered for the * same board and that the OS is not in another section of * code (eg. dfx_xmt_queue_pkt) for the same board on a * different thread. * * Side Effects: * Pending interrupts are serviced. Depending on the type of * interrupt, acknowledging and clearing the interrupt at the * PDQ involves writing a register to clear the interrupt bit * or updating completion indices.
*/
staticvoid dfx_int_common(struct net_device *dev)
{
DFX_board_t *bp = netdev_priv(dev);
PI_UINT32 port_status; /* Port Status register */
/* Process xmt interrupts - frequent case, so always call this routine */
/* Process rcv interrupts - frequent case, so always call this routine */
dfx_rcv_queue_process(bp); /* service received LLC frames */
/* * Transmit and receive producer and completion indices are updated on the * adapter by writing to the Type 2 Producer register. Since the frequent * case is that we'll be processing either LLC transmit or receive buffers, * we'll optimize I/O writes by doing a single register write here.
*/
/* Process Type 0 interrupts (if any) - infrequent, so only call when needed */
if (port_status & PI_PSTATUS_M_TYPE_0_PENDING)
dfx_int_type_0_process(bp); /* process Type 0 interrupts */
}
/* * ================= * = dfx_interrupt = * ================= * * Overview: * Interrupt processing routine * * Returns: * Whether a valid interrupt was seen. * * Arguments: * irq - interrupt vector * dev_id - pointer to device information * * Functional Description: * This routine calls the interrupt processing routine for this adapter. It * disables and reenables adapter interrupts, as appropriate. We can support * shared interrupts since the incoming dev_id pointer provides our device * structure context. * * Return Codes: * IRQ_HANDLED - an IRQ was handled. * IRQ_NONE - no IRQ was handled. * * Assumptions: * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC * on Intel-based systems) is done by the operating system outside this * routine. * * System interrupts are enabled through this call. * * Side Effects: * Interrupts are disabled, then reenabled at the adapter.
*/
status = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0); if (!(status & PI_CONFIG_STAT_0_M_PEND)) return IRQ_NONE;
spin_lock(&bp->lock);
/* Disable interrupts at the ESIC */
status &= ~PI_CONFIG_STAT_0_M_INT_ENB;
outb(status, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
/* Call interrupt service routine for this adapter */
dfx_int_common(dev);
/* Reenable interrupts at the ESIC */
status = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
status |= PI_CONFIG_STAT_0_M_INT_ENB;
outb(status, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
spin_unlock(&bp->lock);
} if (dfx_bus_tc) {
u32 status;
/* Call interrupt service routine for this adapter */
dfx_int_common(dev);
spin_unlock(&bp->lock);
}
return IRQ_HANDLED;
}
/* * ===================== * = dfx_ctl_get_stats = * ===================== * * Overview: * Get statistics for FDDI adapter * * Returns: * Pointer to FDDI statistics structure * * Arguments: * dev - pointer to device information * * Functional Description: * Gets current MIB objects from adapter, then * returns FDDI statistics structure as defined * in if_fddi.h. * * Note: Since the FDDI statistics structure is * still new and the device structure doesn't * have an FDDI-specific get statistics handler, * we'll return the FDDI statistics structure as * a pointer to an Ethernet statistics structure. * That way, at least the first part of the statistics * structure can be decoded properly, and it allows * "smart" applications to perform a second cast to * decode the FDDI-specific statistics. * * We'll have to pay attention to this routine as the * device structure becomes more mature and LAN media * independent. * * Return Codes: * None * * Assumptions: * None * * Side Effects: * None
*/
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.62Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.