/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* This file is part of the Chelsio T4 support code.
*
* Copyright (C) 2010-2013 Chelsio Communications. All rights reserved.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
* release for licensing terms and conditions.
*/
#include "version.h"
#include "t4_l2t.h"
int *rp);
.cb_open = t4_cb_open,
.cb_close = t4_cb_close,
.cb_strategy = nodev,
.cb_ioctl = t4_cb_ioctl,
};
.busops_rev = BUSO_REV,
.bus_ctl = t4_bus_ctl,
};
void **rp);
.devo_identify = nulldev,
.devo_reset = nodev,
.devo_cb_ops = &t4_cb_ops,
.devo_bus_ops = &t4_bus_ops,
};
.drv_modops = &mod_driverops,
};
};
void *t4_list;
struct intrs_and_queues {
#ifndef TCP_OFFLOAD_DISABLE
#endif
};
mblk_t *m);
struct intrs_and_queues *iaq);
#ifndef TCP_OFFLOAD_DISABLE
#endif
#ifndef TCP_OFFLOAD_DISABLE
#endif
int
_init(void)
{
int rc;
if (rc != 0)
return (rc);
if (rc != 0)
#ifndef TCP_OFFLOAD_DISABLE
#endif
return (rc);
}
int
_fini(void)
{
int rc;
if (rc != 0)
return (rc);
return (0);
}
int
{
}
/* ARGSUSED */
static int
{
if (cmd == DDI_INFO_DEVT2DEVINFO) {
return (DDI_FAILURE);
} else if (cmd == DDI_INFO_DEVT2INSTANCE)
else
ASSERT(0);
return (DDI_SUCCESS);
}
static int
{
"device-id", 0xffff);
if (id == 0xffff)
return (DDI_PROBE_DONTCARE);
"reg", ®, &n);
if (rc != DDI_SUCCESS)
return (DDI_PROBE_DONTCARE);
/* Prevent driver attachment on any PF except 0 on the FPGA */
return (DDI_PROBE_FAILURE);
return (DDI_PROBE_DONTCARE);
}
static int
{
struct sge *s;
int irq = 0;
#ifndef TCP_OFFLOAD_DISABLE
#endif
};
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
/*
* Allocate space for soft state.
*/
if (rc != DDI_SUCCESS) {
"failed to allocate soft state: %d", rc);
return (DDI_FAILURE);
}
"failed to determine PCI PF# of device");
goto done;
}
/*
* Enable access to the PCI config space.
*/
if (rc != DDI_SUCCESS) {
"failed to enable PCI config space access: %d", rc);
goto done;
}
/* TODO: Set max read request to 4K */
/*
* Enable MMIO access.
*/
if (rc != DDI_SUCCESS) {
"failed to map device registers: %d", rc);
goto done;
}
/*
* Initialize cpl handler.
*/
}
/*
* Prepare the adapter for operation.
*/
if (rc != 0) {
goto done;
}
/*
* Do this really early. Note that minor number = instance.
*/
DDI_NT_NEXUS, 0);
if (rc != DDI_SUCCESS) {
"failed to create device node: %d", rc);
}
/* Initialize the driver properties */
/* Do this early. Memory window is required for loading config file. */
/* Prepare the firmware for operation */
if (rc != 0)
goto done; /* error message displayed already */
if (rc != 0)
goto done; /* error message displayed already */
/* get basic stuff going */
if (rc != 0) {
"early init failed: %d.\n", rc);
goto done;
}
}
if (rc != 0)
goto done; /* error message displayed already */
/*
* TODO: This is the place to call t4_set_filter_mode()
*/
/* tweak some settings */
/*
* Work-around for bug 2619
* Set DisableVlan field in TP_RSS_CONFIG_VRT register so that the
* VLAN tag extraction is disabled.
*/
/* Store filter mode */
/*
* First pass over all the ports - allocate VIs and initialize some
* basic parameters like mac address, port type, etc. We also figure
* out whether a port is 10G or 1G and use that information when
* calculating how many interrupts to attempt to allocate.
*/
/* These must be set before t4_port_init */
/* LINTED: E_ASSIGN_NARROW_CONV */
/* Allocate the vi and initialize parameters like mac addr */
if (rc != 0) {
"unable to initialize port %d: %d", i, rc);
continue;
}
if (is_10G_port(pi) != 0) {
n10g++;
} else {
n1g++;
}
t4_mc_init(pi);
}
if (sc->registered_device_map == 0) {
rc = DDI_FAILURE;
goto done;
}
if (rc != 0)
goto done; /* error message displayed already */
#ifndef TCP_OFFLOAD_DISABLE
/* control queues, 1 per port + 1 mgmtq */
#endif
#ifndef TCP_OFFLOAD_DISABLE
if (is_offload(sc) != 0) {
sizeof (struct sge_ofld_rxq), KM_SLEEP);
}
#endif
sizeof (ddi_intr_handle_t), KM_SLEEP);
/*
* Second pass over the ports. This time we know the number of rx and
* tx queues that each port should get.
*/
#ifndef TCP_OFFLOAD_DISABLE
ofld_rqidx = ofld_tqidx = 0;
#endif
continue;
/* LINTED: E_ASSIGN_NARROW_CONV */
/* LINTED: E_ASSIGN_NARROW_CONV */
/* LINTED: E_ASSIGN_NARROW_CONV */
/* LINTED: E_ASSIGN_NARROW_CONV */
#ifndef TCP_OFFLOAD_DISABLE
if (is_offload(sc) != 0) {
/* LINTED: E_ASSIGN_NARROW_CONV */
/* LINTED: E_ASSIGN_NARROW_CONV */
}
#endif
/*
* Enable hw checksumming and LSO for all ports by default.
* They can be disabled using ndd (hw_csum and hw_lso).
*/
}
#ifndef TCP_OFFLOAD_DISABLE
#endif
/*
* Setup Interrupts.
*/
i = 0;
if (rc != DDI_SUCCESS) {
"failed to allocate %d interrupt(s) of type %d: %d, %d",
goto done;
}
&s->fwq);
} else {
/* Multiple interrupts. The first one is always error intr */
NULL);
irq++;
/* The second one is always the firmware event queue */
&s->fwq);
irq++;
/*
* Note that if INTR_FWD is set then either the NIC rx
* queues or (exclusive or) the TOE rx queueus will be taking
* direct interrupts.
*
* There is no need to check for is_offload(sc) as nofldrxq
* will be 0 if offload is disabled.
*/
#ifndef TCP_OFFLOAD_DISABLE
/*
* Skip over the NIC queues if they aren't taking direct
* interrupts.
*/
goto ofld_queues;
#endif
(void) ddi_intr_add_handler(
irq++;
}
#ifndef TCP_OFFLOAD_DISABLE
/*
* Skip over the offload queues if they aren't taking
* direct interrupts.
*/
continue;
(void) ddi_intr_add_handler(
irq++;
}
#endif
}
}
"%dx10G %dx1G (%d rxq, %d txq total) %d %s.",
"fixed interrupt");
} else {
"%dx%sG (%d rxq, %d txq per port) %d %s.",
sc->intr_count,
"fixed interrupt");
}
done:
if (rc != DDI_SUCCESS) {
/* rc may have errno style errors or DDI errors */
rc = DDI_FAILURE;
}
return (rc);
}
static int
{
int instance, i;
struct sge *s;
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
(void) port_full_uninit(pi);
}
(void) adapter_full_uninit(sc);
}
/* Safe to call no matter what */
#ifndef TCP_OFFLOAD_DISABLE
s->nofldrxq * sizeof (struct sge_ofld_rxq));
#endif
if (s->rxbuf_cache != NULL)
for (i = 0; i < sc->intr_count; i++) {
}
}
}
}
}
#ifdef DEBUG
#endif
return (DDI_SUCCESS);
}
static int
{
int instance;
return (DDI_SUCCESS);
return (DDI_SUCCESS);
}
static int
void *result)
{
char s[4];
switch (op) {
case DDI_CTLOPS_REPORTDEV:
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
return (DDI_NOT_WELL_FORMED);
ddi_set_name_addr(child, s);
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_ATTACH:
case DDI_CTLOPS_DETACH:
return (DDI_SUCCESS);
default:
}
}
static int
dev_info_t **cdipp)
{
int instance, i;
if (op == BUS_CONFIG_ONE) {
char *c;
/*
* arg is something like "cxgb@0" where 0 is the port_id hanging
* off this nexus.
*/
c = arg;
while (*(c + 1))
c++;
/* There should be exactly 1 digit after '@' */
if (*(c - 1) != '@')
return (NDI_FAILURE);
i = *c - '0';
if (add_child_node(sc, i) != 0)
return (NDI_FAILURE);
/* Allocate and bind all child device nodes */
for_each_port(sc, i)
(void) add_child_node(sc, i);
}
}
static int
void *arg)
{
flags |= NDI_UNCONFIG;
if (rc != 0)
return (rc);
if (op == BUS_UNCONFIG_ONE) {
char *c;
c = arg;
while (*(c + 1))
c++;
if (*(c - 1) != '@')
return (NDI_SUCCESS);
i = *c - '0';
for_each_port(sc, i)
(void) remove_child_node(sc, i);
}
return (rc);
}
/* ARGSUSED */
static int
{
return (EINVAL);
return (ENXIO);
}
/* ARGSUSED */
static int
{
return (EINVAL);
return (0);
}
/* ARGSUSED */
static int
{
int instance;
void *data = (void *)d;
return (EPERM);
return (EINVAL);
}
static unsigned int
{
if (rc != DDI_SUCCESS) {
"failed to lookup \"reg\" property: %d", rc);
return (0xff);
}
return (pf);
}
/*
* Install a compatible firmware (if required), establish contact with it,
* become the master, and reset the device.
*/
static int
{
int rc;
/* Check firmware version and install a different one if necessary */
if (rc != 0) {
uint32_t v;
/* This is what the driver has */
/*
* Downgrade only for a major version mismatch.
*/
"installing firmware %d.%d.%d.%d on card.",
if (rc != 0) {
"failed to install firmware: %d", rc);
return (rc);
} else {
/* refresh */
(void) t4_check_fw_version(sc);
}
}
}
/* Contact firmware, request master */
if (rc < 0) {
"failed to connect to the firmware: %d.", rc);
return (rc);
}
/* Reset device */
if (rc != 0) {
"firmware reset failed: %d.", rc);
return (rc);
}
/* Partition adapter resources as specified in the config file. */
/* Handle default vs special T4 config file */
if (rc != 0)
goto err; /* error message displayed already */
}
return (0);
err:
return (rc);
}
/*
* Upload configuration file to card's memory.
*/
static int
{
int rc, i;
const uint32_t *b;
/* Figure out where the firmware wants us to upload it. */
if (rc != 0) {
/* Firmwares without config file support will fail this way */
"failed to query config file location: %d.\n", rc);
return (rc);
}
if (maddr & 3) {
"cannot upload config file (type %u, addr %x).\n",
return (EFAULT);
}
switch (mtype) {
case FW_MEMTYPE_CF_EDC0:
if (!(val & F_EDRAM0_ENABLE))
goto err;
break;
case FW_MEMTYPE_CF_EDC1:
if (!(val & F_EDRAM1_ENABLE))
goto err;
break;
case FW_MEMTYPE_CF_EXTMEM:
if (!(val & F_EXT_MEM_ENABLE))
goto err;
break;
default:
err:
"cannot upload config file (type %u, enabled %u).\n",
return (EFAULT);
}
/*
* Position the PCIe window (we use memwin2) to the 16B aligned area
*/
(void) t4_read_reg(sc,
if (remaining > FLASH_CFG_MAX_SIZE ||
" once (size %u, max %u, room %u).\n",
return (EFBIG);
}
/*
* TODO: sheer laziness. We deliberately added 4 bytes of useless
* throw away the last remaining bytes when the config file is not an
* exact multiple of 4.
*/
/* LINTED: E_BAD_PTR_CAST_ALIGN */
b = (const uint32_t *)t4cfg_data;
return (rc);
}
/*
* Partition chip resources for use between various PFs, VFs, etc. This is done
* by uploading the firmware configuration file to the adapter and instructing
* the firmware to process it.
*/
static int
{
int rc;
if (rc != 0) {
}
if (rc != 0) {
"failed to pre-process config file: %d.\n", rc);
return (rc);
}
"WARNING: config file checksum mismatch: %08x %08x\n",
}
/* TODO: Need to configure this correctly */
/* TODO: Disable VNIC cap for now */
if (rc != 0) {
"failed to process config file: %d.\n", rc);
return (rc);
}
return (0);
}
/*
* Retrieve parameters that are needed (or nice to have) prior to calling
* t4_sge_init and t4_fw_initialize.
*/
static int
{
int rc;
if (rc != 0) {
"failed to query parameters (pre_init): %d.\n", rc);
return (rc);
}
while (val[0]) {
}
/* Read device log parameters. */
if (rc != 0) {
"failed to get devlog parameters: %d.\n", rc);
rc = 0; /* devlog isn't critical for device operation */
} else {
}
return (rc);
}
/*
* Retrieve various parameters that are of interest to the driver. The device
* has been initialized by the firmware at this point.
*/
static int
{
int rc;
if (rc != 0) {
"failed to query parameters (post_init): %d.\n", rc);
return (rc);
}
/* LINTED: E_ASSIGN_NARROW_CONV */
/* get capabilites */
if (rc != 0) {
"failed to get card capabilities: %d.\n", rc);
return (rc);
}
/* query offload-related parameters */
if (rc != 0) {
"failed to query TOE parameters: %d.\n", rc);
return (rc);
}
}
/* These are finalized by FW initialization, load their values now */
return (rc);
}
/* TODO: verify */
static void
{
int rc;
uint_t n;
if (rc != DDI_SUCCESS) {
"failed to lookup \"assigned-addresses\" property: %d", rc);
return;
}
n /= sizeof (*data);
}
/*
* Reads the named property and fills up the "data" array (which has at least
* "count" elements). We first try and lookup the property for our dev_t and
* then retry with DDI_DEV_T_ANY if it's not found.
*
* Returns non-zero if the property was found and "data" has been updated.
*/
static int
{
int rc, *d;
uint_t i, n;
name, &d, &n);
if (rc == DDI_PROP_SUCCESS)
goto found;
if (rc != DDI_PROP_NOT_FOUND) {
"failed to lookup property %s for minor %d: %d.",
return (0);
}
name, &d, &n);
if (rc == DDI_PROP_SUCCESS)
goto found;
if (rc != DDI_PROP_NOT_FOUND) {
return (0);
}
return (0);
if (n > count) {
"property %s has too many elements (%d), ignoring extras",
name, n);
}
for (i = 0; i < n && i < count; i++)
data[i] = d[i];
ddi_prop_free(d);
return (1);
}
static int
{
int rc;
if (rc != -1)
return (rc);
}
static int
{
int i, *data;
/*
* Holdoff timer
*/
for (i = 0; i < SGE_NTIMERS; i++)
for (i = 0; i < SGE_NTIMERS; i++) {
"holdoff timer %d is too high (%d), lowered to %d.",
}
}
data, SGE_NTIMERS);
/*
* Holdoff packet counter
*/
data = &p->counter_val[0];
for (i = 0; i < SGE_NCOUNTERS; i++)
for (i = 0; i < SGE_NCOUNTERS; i++) {
"holdoff pkt-counter %d is too high (%d), "
}
}
/*
* Maximum # of tx and rx queues to use for each 10G and 1G port.
*/
p->max_ntxq_10g);
p->max_nrxq_10g);
p->max_ntxq_1g);
p->max_nrxq_1g);
#ifndef TCP_OFFLOAD_DISABLE
p->max_nofldtxq_10g);
p->max_nofldrxq_10g);
p->max_nofldtxq_1g);
p->max_nofldrxq_1g);
#endif
/*
* Holdoff parameters for 10G and 1G ports.
*/
p->tmr_idx_10g);
p->pktc_idx_10g);
p->tmr_idx_1g);
p->pktc_idx_1g);
/*
* Size (number of entries) of each tx and rx queue.
*/
if (p->qsize_txq != i) {
"using %d instead of %d as the tx queue size",
p->qsize_txq, i);
}
while (p->qsize_rxq & 7)
p->qsize_rxq--;
if (p->qsize_rxq != i) {
"using %d instead of %d as the rx queue size",
p->qsize_rxq, i);
}
/*
* Interrupt types allowed.
* Bits 0, 1, 2 = INTx, MSI, MSI-X respectively. See sys/ddi_intr.h
*/
/*
* Forwarded interrupt queues. Create this property to force the driver
* to use forwarded interrupt queues.
*/
"interrupt-forwarding") != 0 ||
"interrupt-forwarding") != 0) {
"interrupt-forwarding", NULL, 0);
}
return (0);
}
static int
{
if (n10g == 0) {
"holdoff-timer-idx-10G");
"holdoff-pktc-idx-10G");
}
if (n1g == 0) {
"holdoff-timer-idx-1G");
}
return (0);
}
static int
struct intrs_and_queues *iaq)
{
#ifndef TCP_OFFLOAD_DISABLE
#endif
if (rc != DDI_SUCCESS) {
"failed to determine supported interrupt types: %d", rc);
return (rc);
}
itype == DDI_INTR_TYPE_MSI ||
itype == DDI_INTR_TYPE_FIXED);
continue; /* not supported or not allowed */
navail = 0;
"failed to get # of interrupts for type %d: %d",
continue; /* carry on */
}
#if MAC_VERSION == 1
#endif
if (navail == 0)
continue;
/*
* Best option: an interrupt vector for errors, one for the
* firmware event queue, and one each for each rxq (NIC as well
* as offload).
*/
goto allocate;
}
/*
* Second best option: an interrupt vector for errors, one for
* the firmware event queue, and one each for either NIC or
* offload rxq's.
*/
goto allocate;
}
/*
* Next best option: an interrupt vector for errors, one for the
* firmware event queue, and at least one per port. At this
* point we know we'll have to downsize nrxq or nofldrxq to fit
* what's available to us.
*/
if (n10g > 0) {
n = 1;
n++;
}
#ifndef TCP_OFFLOAD_DISABLE
#endif
}
if (n1g > 0) {
n = 1;
n++;
}
#ifndef TCP_OFFLOAD_DISABLE
#endif
}
goto allocate;
}
}
/*
* Least desirable option: one interrupt vector for everything.
*/
#ifndef TCP_OFFLOAD_DISABLE
#endif
return (0);
}
"failed to find a usable interrupt type. supported=%d, allowed=%d",
itypes, p->intr_types);
return (DDI_FAILURE);
}
static int
{
int rc;
return (EINVAL);
return (ENODEV); /* t4_port_init failed earlier */
rc = 0; /* EEXIST really, but then bus_config fails */
goto done;
}
goto done;
}
rc = 0;
done:
return (rc);
}
static int
{
int rc;
return (EINVAL);
return (ENODEV);
goto done;
}
if (rc == 0)
done:
return (rc);
}
#define KS_C_SET(x, ...) \
/*
* t4nex:X:config
*/
struct t4_kstats {
};
static kstat_t *
{
int ndata;
return (NULL);
}
/* Do NOT set ksp->ks_update. These kstats do not change. */
/* Install the kstat */
return (ksp);
}
int
{
int i, rc = 0;
if (rc != 0)
goto done;
else {
for (i = 0; i < sc->intr_count; i++)
}
#ifndef TCP_OFFLOAD_DISABLE
/* TODO: wrong place to enable TOE capability */
if (is_offload(sc) != 0) {
if (rc != 0) {
"Failed to activate toe capability: %d",
rc);
rc = 0; /* not a fatal error */
}
}
}
#endif
done:
if (rc != 0)
(void) adapter_full_uninit(sc);
return (rc);
}
int
{
int i, rc = 0;
else {
for (i = 0; i < sc->intr_count; i++)
}
if (rc != 0)
return (rc);
return (0);
}
int
{
int rc, i;
/*
*/
if (rc != 0)
goto done; /* error message displayed already */
/*
* Setup RSS for this port.
*/
}
if (rc != 0) {
goto done;
}
done:
if (rc != 0)
(void) port_full_uninit(pi);
return (rc);
}
/*
* Idempotent.
*/
int
{
(void) t4_teardown_port_queues(pi);
return (0);
}
void
{
int i;
#ifndef TCP_OFFLOAD_DISABLE
#endif
/*
* TODO: whatever was queued up after we set iq->state to IQS_DISABLED
* back in disable_port_queues will be processed now, after an unbounded
* delay. This can't be good.
*/
#ifndef TCP_OFFLOAD_DISABLE
(void *)iq);
}
#endif
(void *) iq);
}
}
void
{
int i;
#ifndef TCP_OFFLOAD_DISABLE
#endif
/*
* TODO: need proper implementation for all tx queues (ctrl, eth, ofld).
*/
#ifndef TCP_OFFLOAD_DISABLE
IQS_DISABLED) != IQS_IDLE)
msleep(1);
}
#endif
IQS_DISABLED) != IQS_IDLE)
msleep(1);
}
#ifndef TCP_OFFLOAD_DISABLE
#endif
/* TODO: need to wait for all fl's to be removed from sc->sfl */
}
void
{
"encountered fatal error, adapter stopped.");
}
int
{
if ((stat & PCI_STAT_CAP) == 0)
return (0); /* does not implement capabilities */
while (cap_ptr) {
return (cap_ptr); /* found */
}
return (0); /* not found */
}
void
{
static const char *mod_str[] = {
};
"unknown transceiver inserted.\n");
"unsupported transceiver inserted.\n");
else
}
/* ARGSUSED */
static int
{
if (m != NULL)
freemsg(m);
return (0);
}
int
{
return (EINVAL);
return (0);
}
#ifndef TCP_OFFLOAD_DISABLE
static int
{
int rc;
if (!is_offload(sc))
return (ENODEV);
if (enable != 0) {
return (0);
if (sc->offload_map == 0) {
if (rc != 0)
return (rc);
}
} else {
return (0);
if (sc->offload_map == 0) {
if (rc != 0) {
return (rc);
}
}
}
return (0);
}
/*
* Add an upper layer driver to the global list.
*/
int
{
int rc = 0;
struct uld_info *u;
goto done;
}
}
done:
return (rc);
}
int
{
struct uld_info *u;
if (u == ui) {
goto done;
}
rc = 0;
goto done;
}
}
done:
return (rc);
}
static int
{
if (rc == 0) {
}
goto done;
}
}
done:
return (rc);
}
static int
{
int rc;
goto done;
}
if (rc == 0) {
}
done:
return (rc);
}
void
{
/*
* func should not make any assumptions about what state sc is
* in - the only guarantee is that sc->sc_lock is a valid lock.
*/
}
}
#endif