#include "net_driver.h"
#include <linux/module.h>
#include "efx_common.h"
#include "efx_channels.h"
#include "io.h"
#include "ef100_nic.h"
#include "ef100_netdev.h"
#include "ef100_sriov.h"
#include "ef100_regs.h"
#include "ef100.h"
#define EFX_EF100_PCI_DEFAULT_BAR 2
#define PCI_EXT_CAP_HDR_LENGTH 4
#define ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH 16
struct ef100_func_ctl_window {
bool valid;
unsigned int bar;
u64 offset;
};
static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset,
struct ef100_func_ctl_window *result);
#define ROUND_DOWN_TO_DWORD(x) (((x) & (~31)) >> 3)
#define EXTRACT_BITS(x, lbn, width) \
(((x) >> ((lbn) & 31)) & ((1ull << (width)) - 1))
static u32 _ef100_pci_get_bar_bits_with_width(struct efx_nic *efx,
int structure_start,
int lbn, int width)
{
efx_dword_t dword;
efx_readd(efx, &dword, structure_start + ROUND_DOWN_TO_DWORD(lbn));
return EXTRACT_BITS(le32_to_cpu(dword.u32[0]), lbn, width);
}
#define ef100_pci_get_bar_bits(efx, entry_location, bitdef) \
_ef100_pci_get_bar_bits_with_width(efx, entry_location, \
ESF_GZ_CFGBAR_ ## bitdef ## _LBN, \
ESF_GZ_CFGBAR_ ## bitdef ## _WIDTH)
static int ef100_pci_parse_ef100_entry(struct efx_nic *efx, int entry_location,
struct ef100_func_ctl_window *result)
{
u64 offset = ef100_pci_get_bar_bits(efx, entry_location, EF100_FUNC_CTL_WIN_OFF) <<
ESE_GZ_EF100_FUNC_CTL_WIN_OFF_SHIFT;
u32 bar = ef100_pci_get_bar_bits(efx, entry_location, EF100_BAR);
netif_dbg(efx, probe, efx->net_dev,
"Found EF100 function control window bar=%d offset=0x%llx\n",
bar, offset);
if (result->valid) {
netif_err(efx, probe, efx->net_dev,
"Duplicated EF100 table entry.\n");
return -EINVAL;
}
if (bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_EXPANSION_ROM ||
bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_INVALID) {
netif_err(efx, probe, efx->net_dev,
"Bad BAR value of %d in Xilinx capabilities EF100 entry.\n",
bar);
return -EINVAL;
}
result->bar = bar;
result->offset = offset;
result->valid = true;
return 0;
}
static bool ef100_pci_does_bar_overflow(struct efx_nic *efx, int bar,
u64 next_entry)
{
return next_entry + ESE_GZ_CFGBAR_ENTRY_HEADER_SIZE >
pci_resource_len(efx->pci_dev, bar);
}
static int ef100_pci_parse_continue_entry(struct efx_nic *efx, int entry_location,
struct ef100_func_ctl_window *result)
{
unsigned int previous_bar;
efx_oword_t entry;
u64 offset;
int rc = 0;
u32 bar;
efx_reado(efx, &entry, entry_location);
bar = EFX_OWORD_FIELD32(entry, ESF_GZ_CFGBAR_CONT_CAP_BAR);
offset = EFX_OWORD_FIELD64(entry, ESF_GZ_CFGBAR_CONT_CAP_OFFSET) <<
ESE_GZ_CONT_CAP_OFFSET_BYTES_SHIFT;
previous_bar = efx->mem_bar;
if (bar == ESE_GZ_VSEC_BAR_NUM_EXPANSION_ROM ||
bar == ESE_GZ_VSEC_BAR_NUM_INVALID) {
netif_err(efx, probe, efx->net_dev,
"Bad BAR value of %d in Xilinx capabilities sub-table.\n",
bar);
return -EINVAL;
}
if (bar != previous_bar) {
efx_fini_io(efx);
if (ef100_pci_does_bar_overflow(efx, bar, offset)) {
netif_err(efx, probe, efx->net_dev,
"Xilinx table will overrun BAR[%d] offset=0x%llx\n",
bar, offset);
return -EINVAL;
}
rc = efx_init_io(efx, bar,
(dma_addr_t)DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
pci_resource_len(efx->pci_dev, bar));
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Mapping new BAR for Xilinx table failed, rc=%d\n", rc);
return rc;
}
}
rc = ef100_pci_walk_xilinx_table(efx, offset, result);
if (rc)
return rc;
if (bar != previous_bar) {
efx_fini_io(efx);
rc = efx_init_io(efx, previous_bar,
(dma_addr_t)DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
pci_resource_len(efx->pci_dev, previous_bar));
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Putting old BAR back failed, rc=%d\n", rc);
return rc;
}
}
return 0;
}
static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset,
struct ef100_func_ctl_window *result)
{
u64 current_entry = offset;
int rc = 0;
while (true) {
u32 id = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_FORMAT);
u32 last = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_LAST);
u32 rev = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_REV);
u32 entry_size;
if (id == ESE_GZ_CFGBAR_ENTRY_LAST)
return 0;
entry_size = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_SIZE);
netif_dbg(efx, probe, efx->net_dev,
"Seen Xilinx table entry 0x%x size 0x%x at 0x%llx in BAR[%d]\n",
id, entry_size, current_entry, efx->mem_bar);
if (entry_size < sizeof(u32) * 2) {
netif_err(efx, probe, efx->net_dev,
"Xilinx table entry too short len=0x%x\n", entry_size);
return -EINVAL;
}
switch (id) {
case ESE_GZ_CFGBAR_ENTRY_EF100:
if (rev != ESE_GZ_CFGBAR_ENTRY_REV_EF100 ||
entry_size < ESE_GZ_CFGBAR_ENTRY_SIZE_EF100) {
netif_err(efx, probe, efx->net_dev,
"Bad length or rev for EF100 entry in Xilinx capabilities table. entry_size=%d rev=%d.\n",
entry_size, rev);
return -EINVAL;
}
rc = ef100_pci_parse_ef100_entry(efx, current_entry,
result);
if (rc)
return rc;
break;
case ESE_GZ_CFGBAR_ENTRY_CONT_CAP_ADDR:
if (rev != 0 || entry_size < ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH) {
netif_err(efx, probe, efx->net_dev,
"Bad length or rev for continue entry in Xilinx capabilities table. entry_size=%d rev=%d.\n",
entry_size, rev);
return -EINVAL;
}
rc = ef100_pci_parse_continue_entry(efx, current_entry, result);
if (rc)
return rc;
break;
default:
break;
}
if (last)
return 0;
current_entry += entry_size;
if (ef100_pci_does_bar_overflow(efx, efx->mem_bar, current_entry)) {
netif_err(efx, probe, efx->net_dev,
"Xilinx table overrun at position=0x%llx.\n",
current_entry);
return -EINVAL;
}
}
}
static int _ef100_pci_get_config_bits_with_width(struct efx_nic *efx,
int structure_start, int lbn,
int width, u32 *result)
{
int rc, pos = structure_start + ROUND_DOWN_TO_DWORD(lbn);
u32 temp;
rc = pci_read_config_dword(efx->pci_dev, pos, &temp);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read PCI config dword at %d\n",
pos);
return rc;
}
*result = EXTRACT_BITS(temp, lbn, width);
return 0;
}
#define ef100_pci_get_config_bits(efx, entry_location, bitdef, result) \
_ef100_pci_get_config_bits_with_width(efx, entry_location, \
ESF_GZ_VSEC_ ## bitdef ## _LBN, \
ESF_GZ_VSEC_ ## bitdef ## _WIDTH, result)
static int ef100_pci_parse_xilinx_cap(struct efx_nic *efx, int vndr_cap,
bool has_offset_hi,
struct ef100_func_ctl_window *result)
{
u32 offset_high = 0;
u32 offset_lo = 0;
u64 offset = 0;
u32 bar = 0;
int rc = 0;
rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_BAR, &bar);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_TBL_BAR, rc=%d\n",
rc);
return rc;
}
if (bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_EXPANSION_ROM ||
bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_INVALID) {
netif_err(efx, probe, efx->net_dev,
"Bad BAR value of %d in Xilinx capabilities sub-table.\n",
bar);
return -EINVAL;
}
rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_LO, &offset_lo);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_TBL_OFF_LO, rc=%d\n",
rc);
return rc;
}
if (has_offset_hi) {
rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_HI, &offset_high);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_TBL_OFF_HI, rc=%d\n",
rc);
return rc;
}
}
offset = (((u64)offset_lo) << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT) |
(((u64)offset_high) << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT);
if (offset > pci_resource_len(efx->pci_dev, bar) - sizeof(u32) * 2) {
netif_err(efx, probe, efx->net_dev,
"Xilinx table will overrun BAR[%d] offset=0x%llx\n",
bar, offset);
return -EINVAL;
}
rc = efx_init_io(efx, bar,
(dma_addr_t)DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
pci_resource_len(efx->pci_dev, bar));
if (rc) {
netif_err(efx, probe, efx->net_dev,
"efx_init_io failed, rc=%d\n", rc);
return rc;
}
rc = ef100_pci_walk_xilinx_table(efx, offset, result);
efx_fini_io(efx);
return rc;
}
static int ef100_pci_find_func_ctrl_window(struct efx_nic *efx,
struct ef100_func_ctl_window *result)
{
int num_xilinx_caps = 0;
int cap = 0;
result->valid = false;
while ((cap = pci_find_next_ext_capability(efx->pci_dev, cap, PCI_EXT_CAP_ID_VNDR)) != 0) {
int vndr_cap = cap + PCI_EXT_CAP_HDR_LENGTH;
u32 vsec_ver = 0;
u32 vsec_len = 0;
u32 vsec_id = 0;
int rc = 0;
num_xilinx_caps++;
rc = ef100_pci_get_config_bits(efx, vndr_cap, ID, &vsec_id);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_ID, rc=%d\n",
rc);
return rc;
}
rc = ef100_pci_get_config_bits(efx, vndr_cap, VER, &vsec_ver);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_VER, rc=%d\n",
rc);
return rc;
}
rc = ef100_pci_get_config_bits(efx, vndr_cap, LEN, &vsec_len);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to read ESF_GZ_VSEC_LEN, rc=%d\n",
rc);
return rc;
}
if (vsec_id == ESE_GZ_XILINX_VSEC_ID &&
vsec_ver == ESE_GZ_VSEC_VER_XIL_CFGBAR &&
vsec_len >= ESE_GZ_VSEC_LEN_MIN) {
bool has_offset_hi = (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT);
rc = ef100_pci_parse_xilinx_cap(efx, vndr_cap,
has_offset_hi, result);
if (rc)
return rc;
}
}
if (num_xilinx_caps && !result->valid) {
netif_err(efx, probe, efx->net_dev,
"Seen %d Xilinx tables, but no EF100 entry.\n",
num_xilinx_caps);
return -EINVAL;
}
return 0;
}
static void ef100_pci_remove(struct pci_dev *pci_dev)
{
struct efx_nic *efx = pci_get_drvdata(pci_dev);
struct efx_probe_data *probe_data;
if (!efx)
return;
probe_data = container_of(efx, struct efx_probe_data, efx);
ef100_remove_netdev(probe_data);
#ifdef CONFIG_SFC_SRIOV
efx_fini_struct_tc(efx);
#endif
ef100_remove(efx);
efx_fini_io(efx);
pci_dbg(pci_dev, "shutdown successful\n");
pci_set_drvdata(pci_dev, NULL);
efx_fini_struct(efx);
kfree(probe_data);
};
static int ef100_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *entry)
{
struct ef100_func_ctl_window fcw = { 0 };
struct efx_probe_data *probe_data;
struct efx_nic *efx;
int rc;
probe_data = kzalloc(sizeof(*probe_data), GFP_KERNEL);
if (!probe_data)
return -ENOMEM;
probe_data->pci_dev = pci_dev;
efx = &probe_data->efx;
efx->type = (const struct efx_nic_type *)entry->driver_data;
efx->pci_dev = pci_dev;
pci_set_drvdata(pci_dev, efx);
rc = efx_init_struct(efx, pci_dev);
if (rc)
goto fail;
efx->vi_stride = EF100_DEFAULT_VI_STRIDE;
pci_info(pci_dev, "Solarflare EF100 NIC detected\n");
rc = ef100_pci_find_func_ctrl_window(efx, &fcw);
if (rc) {
pci_err(pci_dev,
"Error looking for ef100 function control window, rc=%d\n",
rc);
goto fail;
}
if (!fcw.valid) {
fcw.bar = EFX_EF100_PCI_DEFAULT_BAR;
fcw.offset = 0;
fcw.valid = true;
}
if (fcw.offset > pci_resource_len(efx->pci_dev, fcw.bar) - ESE_GZ_FCW_LEN) {
pci_err(pci_dev, "Func control window overruns BAR\n");
rc = -EIO;
goto fail;
}
rc = efx_init_io(efx, fcw.bar,
(dma_addr_t)DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
pci_resource_len(efx->pci_dev, fcw.bar));
if (rc)
goto fail;
efx->reg_base = fcw.offset;
rc = efx->type->probe(efx);
if (rc)
goto fail;
efx->state = STATE_PROBED;
rc = ef100_probe_netdev(probe_data);
if (rc)
goto fail;
pci_dbg(pci_dev, "initialisation successful\n");
return 0;
fail:
ef100_pci_remove(pci_dev);
return rc;
}
#ifdef CONFIG_SFC_SRIOV
static int ef100_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
{
struct efx_nic *efx = pci_get_drvdata(dev);
int rc;
if (efx->type->sriov_configure) {
rc = efx->type->sriov_configure(efx, num_vfs);
if (rc)
return rc;
else
return num_vfs;
}
return -ENOENT;
}
#endif
static const struct pci_device_id ef100_pci_table[] = {
{PCI_DEVICE(PCI_VENDOR_ID_XILINX, 0x0100),
.driver_data = (unsigned long) &ef100_pf_nic_type },
{PCI_DEVICE(PCI_VENDOR_ID_XILINX, 0x1100),
.driver_data = (unsigned long) &ef100_vf_nic_type },
{0}
};
struct pci_driver ef100_pci_driver = {
.name = "sfc_ef100",
.id_table = ef100_pci_table,
.probe = ef100_pci_probe,
.remove = ef100_pci_remove,
#ifdef CONFIG_SFC_SRIOV
.sriov_configure = ef100_pci_sriov_configure,
#endif
.err_handler = &efx_err_handlers,
};
MODULE_DEVICE_TABLE