ug 2036 06:18:35 GMT ETag: "aa1945a858d5e2984aa8f70a9027e36450a3cb5f" /**************************************************************************** * Driver for Solarflare network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. * Copyright 2006-2013 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. */ #include #include #include #include #include #include #include #include "net_driver.h" #include "bitfield.h" #include "efx.h" #include "nic.h" #include "ef10_regs.h" #include "farch_regs.h" #include "io.h" #include "workarounds.h" /************************************************************************** * * Generic buffer handling * These buffers are used for interrupt status, MAC stats, etc. * **************************************************************************/ int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, unsigned int len, gfp_t gfp_flags) { buffer->addr = dma_zalloc_coherent(&efx->pci_dev->dev, len, &buffer->dma_addr, gfp_flags); if (!buffer->addr) return -ENOMEM; buffer->len = len; return 0; } void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) { if (buffer->addr) { dma_free_coherent(&efx->pci_dev->dev, buffer->len, buffer->addr, buffer->dma_addr); buffer->addr = NULL; } } /* Check whether an event is present in the eventq at the current * read pointer. Only useful for self-test. */ bool efx_nic_event_present(struct efx_channel *channel) { return efx_event_present(efx_event(channel, channel->eventq_read_ptr)); } void efx_nic_event_test_start(struct efx_channel *channel) { channel->event_test_cpu = -1; smp_wmb(); channel->efx->type->ev_test_generate(channel); } int efx_nic_irq_test_start(struct efx_nic *efx) { efx->last_irq_cpu = -1; smp_wmb(); return efx->type->irq_test_generate(efx); } /* Hook interrupt handler(s) * Try MSI and then legacy interrupts. */ int efx_nic_init_interrupt(struct efx_nic *efx) { struct efx_channel *channel; unsigned int n_irqs; int rc; if (!EFX_INT_MODE_USE_MSI(efx)) { rc = request_irq(efx->legacy_irq, efx->type->irq_handle_legacy, IRQF_SHARED, efx->name, efx); if (rc) { netif_err(efx, drv, efx->net_dev, "failed to hook legacy IRQ %d\n", efx->pci_dev->irq); goto fail1; } return 0; } #ifdef CONFIG_RFS_ACCEL if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { efx->net_dev->rx_cpu_rmap = alloc_irq_cpu_rmap(efx->n_rx_channels); if (!efx->net_dev->rx_cpu_rmap) { rc = -ENOMEM; goto fail1; } } #endif /* Hook MSI or MSI-X interrupt */ n_irqs = 0; efx_for_each_channel(channel, efx) { rc = request_irq(channel->irq, efx->type->irq_handle_msi, IRQF_PROBE_SHARED, /* Not shared */ efx->msi_context[channel->channel].name, &efx->msi_context[channel->channel]); if (rc) { netif_err(efx, drv, efx->net_dev, "failed to hook IRQ %d\n", channel->irq); goto fail2; } ++n_irqs; #ifdef CONFIG_RFS_ACCEL if (efx->interrupt_mode == EFX_INT_MODE_MSIX && channel->channel < efx->n_rx_channels) { rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap, channel->irq); if (rc) goto fail2; } #endif } return 0; fail2: #ifdef CONFIG_RFS_ACCEL free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); efx->net_dev->rx_cpu_rmap = NULL; #endif efx_for_each_channel(channel, efx) { if (n_irqs-- == 0) break; free_irq(channel->irq, &efx->ms