cxgb_main.c revision 167514
1167514Skmacy/************************************************************************** 2167514Skmacy 3167514SkmacyCopyright (c) 2007, Chelsio Inc. 4167514SkmacyAll rights reserved. 5167514Skmacy 6167514SkmacyRedistribution and use in source and binary forms, with or without 7167514Skmacymodification, are permitted provided that the following conditions are met: 8167514Skmacy 9167514Skmacy 1. Redistributions of source code must retain the above copyright notice, 10167514Skmacy this list of conditions and the following disclaimer. 11167514Skmacy 12167514Skmacy 2. Redistributions in binary form must reproduce the above copyright 13167514Skmacy notice, this list of conditions and the following disclaimer in the 14167514Skmacy documentation and/or other materials provided with the distribution. 15167514Skmacy 16167514Skmacy 3. Neither the name of the Chelsio Corporation nor the names of its 17167514Skmacy contributors may be used to endorse or promote products derived from 18167514Skmacy this software without specific prior written permission. 19167514Skmacy 20167514SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21167514SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22167514SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23167514SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24167514SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25167514SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26167514SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27167514SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28167514SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29167514SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30167514SkmacyPOSSIBILITY OF SUCH DAMAGE. 31167514Skmacy 32167514Skmacy***************************************************************************/ 33167514Skmacy 34167514Skmacy#include <sys/cdefs.h> 35167514Skmacy__FBSDID("$FreeBSD: head/sys/dev/cxgb/cxgb_main.c 167514 2007-03-14 02:37:44Z kmacy $"); 36167514Skmacy 37167514Skmacy#include <sys/param.h> 38167514Skmacy#include <sys/systm.h> 39167514Skmacy#include <sys/kernel.h> 40167514Skmacy#include <sys/bus.h> 41167514Skmacy#include <sys/module.h> 42167514Skmacy#include <sys/pciio.h> 43167514Skmacy#include <sys/conf.h> 44167514Skmacy#include <machine/bus.h> 45167514Skmacy#include <machine/resource.h> 46167514Skmacy#include <sys/bus_dma.h> 47167514Skmacy#include <sys/rman.h> 48167514Skmacy#include <sys/ioccom.h> 49167514Skmacy#include <sys/mbuf.h> 50167514Skmacy#include <sys/linker.h> 51167514Skmacy#include <sys/firmware.h> 52167514Skmacy#include <sys/socket.h> 53167514Skmacy#include <sys/sockio.h> 54167514Skmacy#include <sys/smp.h> 55167514Skmacy#include <sys/sysctl.h> 56167514Skmacy#include <sys/queue.h> 57167514Skmacy#include <sys/taskqueue.h> 58167514Skmacy 59167514Skmacy 60167514Skmacy 61167514Skmacy#include <net/bpf.h> 62167514Skmacy#include <net/ethernet.h> 63167514Skmacy#include <net/if.h> 64167514Skmacy#include <net/if_arp.h> 65167514Skmacy#include <net/if_dl.h> 66167514Skmacy#include <net/if_media.h> 67167514Skmacy#include <net/if_types.h> 68167514Skmacy 69167514Skmacy#include <netinet/in_systm.h> 70167514Skmacy#include <netinet/in.h> 71167514Skmacy#include <netinet/if_ether.h> 72167514Skmacy#include <netinet/ip.h> 73167514Skmacy#include <netinet/ip.h> 74167514Skmacy#include <netinet/tcp.h> 75167514Skmacy#include <netinet/udp.h> 76167514Skmacy 77167514Skmacy#include <dev/pci/pcireg.h> 78167514Skmacy#include <dev/pci/pcivar.h> 79167514Skmacy#include <dev/pci/pci_private.h> 80167514Skmacy 81167514Skmacy#include <dev/cxgb/cxgb_osdep.h> 82167514Skmacy#include <dev/cxgb/common/cxgb_common.h> 83167514Skmacy#include <dev/cxgb/cxgb_ioctl.h> 84167514Skmacy#include <dev/cxgb/common/cxgb_regs.h> 85167514Skmacy#include <dev/cxgb/common/cxgb_t3_cpl.h> 86167514Skmacy#include <dev/cxgb/common/cxgb_firmware_exports.h> 87167514Skmacy 88167514Skmacy 89167514Skmacy#ifdef PRIV_SUPPORTED 90167514Skmacy#include <sys/priv.h> 91167514Skmacy#endif 92167514Skmacy 93167514Skmacystatic int cxgb_setup_msix(adapter_t *, int); 94167514Skmacystatic void cxgb_init(void *); 95167514Skmacystatic void cxgb_init_locked(struct port_info *); 96167514Skmacystatic void cxgb_stop(struct port_info *); 97167514Skmacystatic void cxgb_set_rxmode(struct port_info *); 98167514Skmacystatic int cxgb_ioctl(struct ifnet *, unsigned long, caddr_t); 99167514Skmacystatic void cxgb_start(struct ifnet *); 100167514Skmacystatic void cxgb_start_proc(void *, int ncount); 101167514Skmacystatic int cxgb_media_change(struct ifnet *); 102167514Skmacystatic void cxgb_media_status(struct ifnet *, struct ifmediareq *); 103167514Skmacystatic int setup_sge_qsets(adapter_t *); 104167514Skmacystatic void cxgb_async_intr(void *); 105167514Skmacystatic void cxgb_ext_intr_handler(void *, int); 106167514Skmacystatic void cxgb_tick(void *); 107167514Skmacystatic void check_link_status(adapter_t *sc); 108167514Skmacystatic void setup_rss(adapter_t *sc); 109167514Skmacy 110167514Skmacy/* Attachment glue for the PCI controller end of the device. Each port of 111167514Skmacy * the device is attached separately, as defined later. 112167514Skmacy */ 113167514Skmacystatic int cxgb_controller_probe(device_t); 114167514Skmacystatic int cxgb_controller_attach(device_t); 115167514Skmacystatic int cxgb_controller_detach(device_t); 116167514Skmacystatic void cxgb_free(struct adapter *); 117167514Skmacystatic __inline void reg_block_dump(struct adapter *ap, uint8_t *buf, unsigned int start, 118167514Skmacy unsigned int end); 119167514Skmacystatic void cxgb_get_regs(adapter_t *sc, struct ifconf_regs *regs, uint8_t *buf); 120167514Skmacystatic int cxgb_get_regs_len(void); 121167514Skmacy 122167514Skmacystatic device_method_t cxgb_controller_methods[] = { 123167514Skmacy DEVMETHOD(device_probe, cxgb_controller_probe), 124167514Skmacy DEVMETHOD(device_attach, cxgb_controller_attach), 125167514Skmacy DEVMETHOD(device_detach, cxgb_controller_detach), 126167514Skmacy 127167514Skmacy /* bus interface */ 128167514Skmacy DEVMETHOD(bus_print_child, bus_generic_print_child), 129167514Skmacy DEVMETHOD(bus_driver_added, bus_generic_driver_added), 130167514Skmacy 131167514Skmacy { 0, 0 } 132167514Skmacy}; 133167514Skmacy 134167514Skmacystatic driver_t cxgb_controller_driver = { 135167514Skmacy "cxgbc", 136167514Skmacy cxgb_controller_methods, 137167514Skmacy sizeof(struct adapter) 138167514Skmacy}; 139167514Skmacy 140167514Skmacystatic devclass_t cxgb_controller_devclass; 141167514SkmacyDRIVER_MODULE(cxgbc, pci, cxgb_controller_driver, cxgb_controller_devclass, 0, 0); 142167514Skmacy 143167514Skmacy/* 144167514Skmacy * Attachment glue for the ports. Attachment is done directly to the 145167514Skmacy * controller device. 146167514Skmacy */ 147167514Skmacystatic int cxgb_port_probe(device_t); 148167514Skmacystatic int cxgb_port_attach(device_t); 149167514Skmacystatic int cxgb_port_detach(device_t); 150167514Skmacy 151167514Skmacystatic device_method_t cxgb_port_methods[] = { 152167514Skmacy DEVMETHOD(device_probe, cxgb_port_probe), 153167514Skmacy DEVMETHOD(device_attach, cxgb_port_attach), 154167514Skmacy DEVMETHOD(device_detach, cxgb_port_detach), 155167514Skmacy { 0, 0 } 156167514Skmacy}; 157167514Skmacy 158167514Skmacystatic driver_t cxgb_port_driver = { 159167514Skmacy "cxgb", 160167514Skmacy cxgb_port_methods, 161167514Skmacy 0 162167514Skmacy}; 163167514Skmacy 164167514Skmacystatic d_ioctl_t cxgb_extension_ioctl; 165167514Skmacy 166167514Skmacystatic devclass_t cxgb_port_devclass; 167167514SkmacyDRIVER_MODULE(cxgb, cxgbc, cxgb_port_driver, cxgb_port_devclass, 0, 0); 168167514Skmacy 169167514Skmacy#define SGE_MSIX_COUNT (SGE_QSETS + 1) 170167514Skmacy 171167514Skmacy/* 172167514Skmacy * The driver uses the best interrupt scheme available on a platform in the 173167514Skmacy * order MSI-X, MSI, legacy pin interrupts. This parameter determines which 174167514Skmacy * of these schemes the driver may consider as follows: 175167514Skmacy * 176167514Skmacy * msi = 2: choose from among all three options 177167514Skmacy * msi = 1 : only consider MSI and pin interrupts 178167514Skmacy * msi = 0: force pin interrupts 179167514Skmacy */ 180167514Skmacystatic int msi_allowed = 0; 181167514SkmacyTUNABLE_INT("hw.cxgb.msi_allowed", &msi_allowed); 182167514Skmacy 183167514SkmacySYSCTL_NODE(_hw, OID_AUTO, cxgb, CTLFLAG_RD, 0, "CXGB driver parameters"); 184167514SkmacySYSCTL_UINT(_hw_cxgb, OID_AUTO, msi_allowed, CTLFLAG_RDTUN, &msi_allowed, 0, 185167514Skmacy "MSI-X, MSI, INTx selector"); 186167514Skmacy 187167514Skmacyenum { 188167514Skmacy MAX_TXQ_ENTRIES = 16384, 189167514Skmacy MAX_CTRL_TXQ_ENTRIES = 1024, 190167514Skmacy MAX_RSPQ_ENTRIES = 16384, 191167514Skmacy MAX_RX_BUFFERS = 16384, 192167514Skmacy MAX_RX_JUMBO_BUFFERS = 16384, 193167514Skmacy MIN_TXQ_ENTRIES = 4, 194167514Skmacy MIN_CTRL_TXQ_ENTRIES = 4, 195167514Skmacy MIN_RSPQ_ENTRIES = 32, 196167514Skmacy MIN_FL_ENTRIES = 32 197167514Skmacy}; 198167514Skmacy 199167514Skmacy#define PORT_MASK ((1 << MAX_NPORTS) - 1) 200167514Skmacy 201167514Skmacy/* Table for probing the cards. The desc field isn't actually used */ 202167514Skmacystruct cxgb_ident { 203167514Skmacy uint16_t vendor; 204167514Skmacy uint16_t device; 205167514Skmacy int index; 206167514Skmacy char *desc; 207167514Skmacy} cxgb_identifiers[] = { 208167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0020, 0, "PE9000"}, 209167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0021, 1, "T302E"}, 210167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0022, 2, "T310E"}, 211167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0023, 3, "T320X"}, 212167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0024, 1, "T302X"}, 213167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0025, 3, "T320E"}, 214167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0026, 2, "T310X"}, 215167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0030, 2, "T3B10"}, 216167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0031, 3, "T3B20"}, 217167514Skmacy {PCI_VENDOR_ID_CHELSIO, 0x0032, 1, "T3B02"}, 218167514Skmacy {0, 0, 0, NULL} 219167514Skmacy}; 220167514Skmacy 221167514Skmacystatic struct cxgb_ident * 222167514Skmacycxgb_get_ident(device_t dev) 223167514Skmacy{ 224167514Skmacy struct cxgb_ident *id; 225167514Skmacy 226167514Skmacy for (id = cxgb_identifiers; id->desc != NULL; id++) { 227167514Skmacy if ((id->vendor == pci_get_vendor(dev)) && 228167514Skmacy (id->device == pci_get_device(dev))) { 229167514Skmacy return (id); 230167514Skmacy } 231167514Skmacy } 232167514Skmacy return (NULL); 233167514Skmacy} 234167514Skmacy 235167514Skmacystatic const struct adapter_info * 236167514Skmacycxgb_get_adapter_info(device_t dev) 237167514Skmacy{ 238167514Skmacy struct cxgb_ident *id; 239167514Skmacy const struct adapter_info *ai; 240167514Skmacy 241167514Skmacy id = cxgb_get_ident(dev); 242167514Skmacy if (id == NULL) 243167514Skmacy return (NULL); 244167514Skmacy 245167514Skmacy ai = t3_get_adapter_info(id->index); 246167514Skmacy 247167514Skmacy return (ai); 248167514Skmacy} 249167514Skmacy 250167514Skmacystatic int 251167514Skmacycxgb_controller_probe(device_t dev) 252167514Skmacy{ 253167514Skmacy const struct adapter_info *ai; 254167514Skmacy char *ports, buf[80]; 255167514Skmacy 256167514Skmacy ai = cxgb_get_adapter_info(dev); 257167514Skmacy if (ai == NULL) 258167514Skmacy return (ENXIO); 259167514Skmacy 260167514Skmacy if (ai->nports == 1) 261167514Skmacy ports = "port"; 262167514Skmacy else 263167514Skmacy ports = "ports"; 264167514Skmacy 265167514Skmacy snprintf(buf, sizeof(buf), "%s RNIC, %d %s", ai->desc, ai->nports, ports); 266167514Skmacy device_set_desc_copy(dev, buf); 267167514Skmacy return (BUS_PROBE_DEFAULT); 268167514Skmacy} 269167514Skmacy 270167514Skmacystatic int 271167514Skmacycxgb_fw_download(adapter_t *sc, device_t dev) 272167514Skmacy{ 273167514Skmacy char buf[32]; 274167514Skmacy#ifdef FIRMWARE_LATEST 275167514Skmacy const struct firmware *fw; 276167514Skmacy#else 277167514Skmacy struct firmware *fw; 278167514Skmacy#endif 279167514Skmacy int status; 280167514Skmacy 281167514Skmacy snprintf(&buf[0], sizeof(buf), "t3fw%d%d", CHELSIO_FW_MAJOR, CHELSIO_FW_MINOR); 282167514Skmacy 283167514Skmacy fw = firmware_get(buf); 284167514Skmacy 285167514Skmacy 286167514Skmacy if (fw == NULL) { 287167514Skmacy device_printf(dev, "Could not find firmware image %s\n", buf); 288167514Skmacy return ENOENT; 289167514Skmacy } 290167514Skmacy 291167514Skmacy status = t3_load_fw(sc, (const uint8_t *)fw->data, fw->datasize); 292167514Skmacy 293167514Skmacy firmware_put(fw, FIRMWARE_UNLOAD); 294167514Skmacy 295167514Skmacy return (status); 296167514Skmacy} 297167514Skmacy 298167514Skmacy 299167514Skmacystatic int 300167514Skmacycxgb_controller_attach(device_t dev) 301167514Skmacy{ 302167514Skmacy driver_intr_t *cxgb_intr = NULL; 303167514Skmacy device_t child; 304167514Skmacy const struct adapter_info *ai; 305167514Skmacy struct adapter *sc; 306167514Skmacy int i, msi_count = 0, error = 0; 307167514Skmacy uint32_t vers; 308167514Skmacy 309167514Skmacy sc = device_get_softc(dev); 310167514Skmacy sc->dev = dev; 311167514Skmacy 312167514Skmacy pci_enable_busmaster(dev); 313167514Skmacy 314167514Skmacy /* 315167514Skmacy * Allocate the registers and make them available to the driver. 316167514Skmacy * The registers that we care about for NIC mode are in BAR 0 317167514Skmacy */ 318167514Skmacy sc->regs_rid = PCIR_BAR(0); 319167514Skmacy if ((sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 320167514Skmacy &sc->regs_rid, RF_ACTIVE)) == NULL) { 321167514Skmacy device_printf(dev, "Cannot allocate BAR\n"); 322167514Skmacy return (ENXIO); 323167514Skmacy } 324167514Skmacy 325167514Skmacy mtx_init(&sc->sge.reg_lock, "SGE reg lock", NULL, MTX_DEF); 326167514Skmacy mtx_init(&sc->lock, "cxgb controller lock", NULL, MTX_DEF); 327167514Skmacy mtx_init(&sc->mdio_lock, "cxgb mdio", NULL, MTX_DEF); 328167514Skmacy 329167514Skmacy sc->bt = rman_get_bustag(sc->regs_res); 330167514Skmacy sc->bh = rman_get_bushandle(sc->regs_res); 331167514Skmacy sc->mmio_len = rman_get_size(sc->regs_res); 332167514Skmacy 333167514Skmacy /* Allocate the BAR for doing MSI-X. If it succeeds, try to allocate 334167514Skmacy * enough messages for the queue sets. If that fails, try falling 335167514Skmacy * back to MSI. If that fails, then try falling back to the legacy 336167514Skmacy * interrupt pin model. 337167514Skmacy */ 338167514Skmacy#ifdef MSI_SUPPORTED 339167514Skmacy sc->msix_regs_rid = 0x20; 340167514Skmacy if ((msi_allowed >= 2) && 341167514Skmacy (sc->msix_regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 342167514Skmacy &sc->msix_regs_rid, RF_ACTIVE)) != NULL) { 343167514Skmacy 344167514Skmacy msi_count = SGE_MSIX_COUNT; 345167514Skmacy if ((pci_alloc_msix(dev, &msi_count) != 0) || 346167514Skmacy (msi_count != SGE_MSIX_COUNT)) { 347167514Skmacy msi_count = 0; 348167514Skmacy pci_release_msi(dev); 349167514Skmacy bus_release_resource(dev, SYS_RES_MEMORY, 350167514Skmacy sc->msix_regs_rid, sc->msix_regs_res); 351167514Skmacy sc->msix_regs_res = NULL; 352167514Skmacy } else { 353167514Skmacy sc->flags |= USING_MSIX; 354167514Skmacy cxgb_intr = t3_intr_msix; 355167514Skmacy } 356167514Skmacy 357167514Skmacy printf("allocated %d msix intrs\n", msi_count); 358167514Skmacy } 359167514Skmacy 360167514Skmacy if ((msi_allowed >= 1) && (msi_count == 0)) { 361167514Skmacy msi_count = 1; 362167514Skmacy if (pci_alloc_msi(dev, &msi_count)) { 363167514Skmacy device_printf(dev, "alloc msi failed\n"); 364167514Skmacy msi_count = 0; 365167514Skmacy pci_release_msi(dev); 366167514Skmacy } else { 367167514Skmacy sc->flags |= USING_MSI; 368167514Skmacy sc->irq_rid = 1; 369167514Skmacy cxgb_intr = t3_intr_msi; 370167514Skmacy } 371167514Skmacy } 372167514Skmacy#endif 373167514Skmacy if (msi_count == 0) { 374167514Skmacy sc->irq_rid = 0; 375167514Skmacy cxgb_intr = t3b_intr; 376167514Skmacy } 377167514Skmacy 378167514Skmacy 379167514Skmacy /* Create a private taskqueue thread for handling driver events */ 380167514Skmacy#ifdef TASKQUEUE_CURRENT 381167514Skmacy sc->tq = taskqueue_create("cxgb_taskq", M_NOWAIT, 382167514Skmacy taskqueue_thread_enqueue, &sc->tq); 383167514Skmacy#else 384167514Skmacy sc->tq = taskqueue_create_fast("cxgb_taskq", M_NOWAIT, 385167514Skmacy taskqueue_thread_enqueue, &sc->tq); 386167514Skmacy#endif 387167514Skmacy if (sc->tq == NULL) { 388167514Skmacy device_printf(dev, "failed to allocate controller task queue\n"); 389167514Skmacy goto out; 390167514Skmacy } 391167514Skmacy 392167514Skmacy taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq", 393167514Skmacy device_get_nameunit(dev)); 394167514Skmacy TASK_INIT(&sc->ext_intr_task, 0, cxgb_ext_intr_handler, sc); 395167514Skmacy 396167514Skmacy 397167514Skmacy /* Create a periodic callout for checking adapter status */ 398167514Skmacy callout_init_mtx(&sc->cxgb_tick_ch, &sc->lock, 0); 399167514Skmacy 400167514Skmacy ai = cxgb_get_adapter_info(dev); 401167514Skmacy if (t3_prep_adapter(sc, ai, 1) < 0) { 402167514Skmacy error = ENODEV; 403167514Skmacy goto out; 404167514Skmacy } 405167514Skmacy if (t3_check_fw_version(sc) != 0) { 406167514Skmacy /* 407167514Skmacy * Warn user that a firmware update will be attempted in init. 408167514Skmacy */ 409167514Skmacy device_printf(dev, "firmware needs to be updated to version %d.%d\n", 410167514Skmacy CHELSIO_FW_MAJOR, CHELSIO_FW_MINOR); 411167514Skmacy sc->flags &= ~FW_UPTODATE; 412167514Skmacy } else { 413167514Skmacy sc->flags |= FW_UPTODATE; 414167514Skmacy } 415167514Skmacy 416167514Skmacy if (t3_init_hw(sc, 0) != 0) { 417167514Skmacy device_printf(dev, "hw initialization failed\n"); 418167514Skmacy error = ENXIO; 419167514Skmacy goto out; 420167514Skmacy } 421167514Skmacy t3_write_reg(sc, A_ULPRX_TDDP_PSZ, V_HPZ0(PAGE_SHIFT - 12)); 422167514Skmacy 423167514Skmacy /* 424167514Skmacy * Create a child device for each MAC. The ethernet attachment 425167514Skmacy * will be done in these children. 426167514Skmacy */ 427167514Skmacy for (i = 0; i < (sc)->params.nports; ++i) { 428167514Skmacy if ((child = device_add_child(dev, "cxgb", -1)) == NULL) { 429167514Skmacy device_printf(dev, "failed to add child port\n"); 430167514Skmacy error = EINVAL; 431167514Skmacy goto out; 432167514Skmacy } 433167514Skmacy sc->portdev[i] = child; 434167514Skmacy sc->port[i].adapter = sc; 435167514Skmacy#ifdef MULTIQ 436167514Skmacy sc->port[i].nqsets = mp_ncpus; 437167514Skmacy#else 438167514Skmacy sc->port[i].nqsets = 1; 439167514Skmacy#endif 440167514Skmacy sc->port[i].first_qset = i; 441167514Skmacy sc->port[i].port = i; 442167514Skmacy device_set_softc(child, &sc->port[i]); 443167514Skmacy } 444167514Skmacy if ((error = bus_generic_attach(dev)) != 0) 445167514Skmacy goto out;; 446167514Skmacy 447167514Skmacy if ((error = setup_sge_qsets(sc)) != 0) 448167514Skmacy goto out; 449167514Skmacy 450167514Skmacy setup_rss(sc); 451167514Skmacy 452167514Skmacy /* If it's MSI or INTx, allocate a single interrupt for everything */ 453167514Skmacy if ((sc->flags & USING_MSIX) == 0) { 454167514Skmacy if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 455167514Skmacy &sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { 456167514Skmacy device_printf(dev, "Cannot allocate interrupt rid=%d\n", sc->irq_rid); 457167514Skmacy error = EINVAL; 458167514Skmacy goto out; 459167514Skmacy } 460167514Skmacy device_printf(dev, "allocated irq_res=%p\n", sc->irq_res); 461167514Skmacy 462167514Skmacy if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET, 463167514Skmacy#ifdef INTR_FILTERS 464167514Skmacy NULL, 465167514Skmacy#endif 466167514Skmacy cxgb_intr, sc, &sc->intr_tag)) { 467167514Skmacy device_printf(dev, "Cannot set up interrupt\n"); 468167514Skmacy error = EINVAL; 469167514Skmacy goto out; 470167514Skmacy } 471167514Skmacy } else { 472167514Skmacy cxgb_setup_msix(sc, msi_count); 473167514Skmacy } 474167514Skmacy 475167514Skmacy sc->params.stats_update_period = 1; 476167514Skmacy 477167514Skmacy /* initialize sge private state */ 478167514Skmacy t3_sge_init_sw(sc); 479167514Skmacy 480167514Skmacy t3_led_ready(sc); 481167514Skmacy 482167514Skmacy error = t3_get_fw_version(sc, &vers); 483167514Skmacy if (error) 484167514Skmacy goto out; 485167514Skmacy 486167514Skmacy snprintf(&sc->fw_version[0], sizeof(sc->fw_version), "%d.%d", G_FW_VERSION_MAJOR(vers), 487167514Skmacy G_FW_VERSION_MINOR(vers)); 488167514Skmacy 489167514Skmacy t3_add_sysctls(sc); 490167514Skmacy 491167514Skmacyout: 492167514Skmacy if (error) 493167514Skmacy cxgb_free(sc); 494167514Skmacy 495167514Skmacy return (error); 496167514Skmacy} 497167514Skmacy 498167514Skmacystatic int 499167514Skmacycxgb_controller_detach(device_t dev) 500167514Skmacy{ 501167514Skmacy struct adapter *sc; 502167514Skmacy 503167514Skmacy sc = device_get_softc(dev); 504167514Skmacy 505167514Skmacy cxgb_free(sc); 506167514Skmacy 507167514Skmacy return (0); 508167514Skmacy} 509167514Skmacy 510167514Skmacystatic void 511167514Skmacycxgb_free(struct adapter *sc) 512167514Skmacy{ 513167514Skmacy int i; 514167514Skmacy 515167514Skmacy for (i = 0; i < (sc)->params.nports; ++i) { 516167514Skmacy if (sc->portdev[i] != NULL) 517167514Skmacy device_delete_child(sc->dev, sc->portdev[i]); 518167514Skmacy } 519167514Skmacy 520167514Skmacy t3_sge_deinit_sw(sc); 521167514Skmacy 522167514Skmacy if (sc->tq != NULL) { 523167514Skmacy taskqueue_drain(sc->tq, &sc->ext_intr_task); 524167514Skmacy taskqueue_free(sc->tq); 525167514Skmacy } 526167514Skmacy 527167514Skmacy callout_drain(&sc->cxgb_tick_ch); 528167514Skmacy 529167514Skmacy bus_generic_detach(sc->dev); 530167514Skmacy 531167514Skmacy t3_free_sge_resources(sc); 532167514Skmacy t3_sge_free(sc); 533167514Skmacy 534167514Skmacy for (i = 0; i < SGE_QSETS; i++) { 535167514Skmacy if (sc->msix_intr_tag[i] != NULL) { 536167514Skmacy bus_teardown_intr(sc->dev, sc->msix_irq_res[i], 537167514Skmacy sc->msix_intr_tag[i]); 538167514Skmacy } 539167514Skmacy if (sc->msix_irq_res[i] != NULL) { 540167514Skmacy bus_release_resource(sc->dev, SYS_RES_IRQ, 541167514Skmacy sc->msix_irq_rid[i], sc->msix_irq_res[i]); 542167514Skmacy } 543167514Skmacy } 544167514Skmacy 545167514Skmacy if (sc->intr_tag != NULL) { 546167514Skmacy bus_teardown_intr(sc->dev, sc->irq_res, sc->intr_tag); 547167514Skmacy } 548167514Skmacy 549167514Skmacy if (sc->irq_res != NULL) { 550167514Skmacy device_printf(sc->dev, "de-allocating interrupt irq_rid=%d irq_res=%p\n", 551167514Skmacy sc->irq_rid, sc->irq_res); 552167514Skmacy bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid, 553167514Skmacy sc->irq_res); 554167514Skmacy } 555167514Skmacy#ifdef MSI_SUPPORTED 556167514Skmacy if (sc->flags & (USING_MSI | USING_MSIX)) { 557167514Skmacy device_printf(sc->dev, "releasing msi message(s)\n"); 558167514Skmacy pci_release_msi(sc->dev); 559167514Skmacy } 560167514Skmacy#endif 561167514Skmacy if (sc->msix_regs_res != NULL) { 562167514Skmacy bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->msix_regs_rid, 563167514Skmacy sc->msix_regs_res); 564167514Skmacy } 565167514Skmacy 566167514Skmacy if (sc->regs_res != NULL) 567167514Skmacy bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->regs_rid, 568167514Skmacy sc->regs_res); 569167514Skmacy 570167514Skmacy mtx_destroy(&sc->mdio_lock); 571167514Skmacy mtx_destroy(&sc->sge.reg_lock); 572167514Skmacy mtx_destroy(&sc->lock); 573167514Skmacy 574167514Skmacy return; 575167514Skmacy} 576167514Skmacy 577167514Skmacy/** 578167514Skmacy * setup_sge_qsets - configure SGE Tx/Rx/response queues 579167514Skmacy * @sc: the controller softc 580167514Skmacy * 581167514Skmacy * Determines how many sets of SGE queues to use and initializes them. 582167514Skmacy * We support multiple queue sets per port if we have MSI-X, otherwise 583167514Skmacy * just one queue set per port. 584167514Skmacy */ 585167514Skmacystatic int 586167514Skmacysetup_sge_qsets(adapter_t *sc) 587167514Skmacy{ 588167514Skmacy int i, j, err, irq_idx, qset_idx; 589167514Skmacy u_int ntxq = 3; 590167514Skmacy 591167514Skmacy if ((err = t3_sge_alloc(sc)) != 0) { 592167514Skmacy printf("t3_sge_alloc returned %d\n", err); 593167514Skmacy return (err); 594167514Skmacy } 595167514Skmacy 596167514Skmacy if (sc->params.rev > 0 && !(sc->flags & USING_MSI)) 597167514Skmacy irq_idx = -1; 598167514Skmacy else 599167514Skmacy irq_idx = 0; 600167514Skmacy 601167514Skmacy for (qset_idx = 0, i = 0; i < (sc)->params.nports; ++i) { 602167514Skmacy struct port_info *pi = &sc->port[i]; 603167514Skmacy 604167514Skmacy for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { 605167514Skmacy err = t3_sge_alloc_qset(sc, qset_idx, 1, 606167514Skmacy (sc->flags & USING_MSIX) ? qset_idx + 1 : irq_idx, 607167514Skmacy &sc->params.sge.qset[qset_idx], ntxq, pi); 608167514Skmacy if (err) { 609167514Skmacy t3_free_sge_resources(sc); 610167514Skmacy printf("t3_sge_alloc_qset failed with %d\n", err); 611167514Skmacy return (err); 612167514Skmacy } 613167514Skmacy } 614167514Skmacy } 615167514Skmacy 616167514Skmacy return (0); 617167514Skmacy} 618167514Skmacy 619167514Skmacystatic int 620167514Skmacycxgb_setup_msix(adapter_t *sc, int msix_count) 621167514Skmacy{ 622167514Skmacy int i, j, k, nqsets, rid; 623167514Skmacy 624167514Skmacy /* The first message indicates link changes and error conditions */ 625167514Skmacy sc->irq_rid = 1; 626167514Skmacy if ((sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, 627167514Skmacy &sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { 628167514Skmacy device_printf(sc->dev, "Cannot allocate msix interrupt\n"); 629167514Skmacy return (EINVAL); 630167514Skmacy } 631167514Skmacy if (bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET, 632167514Skmacy#ifdef INTR_FILTERS 633167514Skmacy NULL, 634167514Skmacy#endif 635167514Skmacy cxgb_async_intr, sc, &sc->intr_tag)) { 636167514Skmacy device_printf(sc->dev, "Cannot set up interrupt\n"); 637167514Skmacy return (EINVAL); 638167514Skmacy } 639167514Skmacy 640167514Skmacy for (i = 0, k = 0; i < (sc)->params.nports; ++i) { 641167514Skmacy nqsets = sc->port[i].nqsets; 642167514Skmacy for (j = 0; j < nqsets; ++j, k++) { 643167514Skmacy struct sge_qset *qs = &sc->sge.qs[k]; 644167514Skmacy 645167514Skmacy rid = k + 2; 646167514Skmacy if (cxgb_debug) 647167514Skmacy printf("rid=%d ", rid); 648167514Skmacy if ((sc->msix_irq_res[k] = bus_alloc_resource_any( 649167514Skmacy sc->dev, SYS_RES_IRQ, &rid, 650167514Skmacy RF_SHAREABLE | RF_ACTIVE)) == NULL) { 651167514Skmacy device_printf(sc->dev, "Cannot allocate " 652167514Skmacy "interrupt for message %d\n", rid); 653167514Skmacy return (EINVAL); 654167514Skmacy } 655167514Skmacy sc->msix_irq_rid[k] = rid; 656167514Skmacy if (bus_setup_intr(sc->dev, sc->msix_irq_res[j], 657167514Skmacy INTR_MPSAFE|INTR_TYPE_NET, 658167514Skmacy#ifdef INTR_FILTERS 659167514Skmacy NULL, 660167514Skmacy#endif 661167514Skmacy t3_intr_msix, qs, &sc->msix_intr_tag[k])) { 662167514Skmacy device_printf(sc->dev, "Cannot set up " 663167514Skmacy "interrupt for message %d\n", rid); 664167514Skmacy return (EINVAL); 665167514Skmacy } 666167514Skmacy } 667167514Skmacy } 668167514Skmacy return (0); 669167514Skmacy} 670167514Skmacy 671167514Skmacystatic int 672167514Skmacycxgb_port_probe(device_t dev) 673167514Skmacy{ 674167514Skmacy struct port_info *p; 675167514Skmacy char buf[80]; 676167514Skmacy 677167514Skmacy p = device_get_softc(dev); 678167514Skmacy 679167514Skmacy snprintf(buf, sizeof(buf), "Port %d %s", p->port, p->port_type->desc); 680167514Skmacy device_set_desc_copy(dev, buf); 681167514Skmacy return (0); 682167514Skmacy} 683167514Skmacy 684167514Skmacy 685167514Skmacystatic int 686167514Skmacycxgb_makedev(struct port_info *pi) 687167514Skmacy{ 688167514Skmacy struct cdevsw *cxgb_cdevsw; 689167514Skmacy 690167514Skmacy if ((cxgb_cdevsw = malloc(sizeof(struct cdevsw), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 691167514Skmacy return (ENOMEM); 692167514Skmacy 693167514Skmacy cxgb_cdevsw->d_version = D_VERSION; 694167514Skmacy cxgb_cdevsw->d_name = strdup(pi->ifp->if_xname, M_DEVBUF); 695167514Skmacy cxgb_cdevsw->d_ioctl = cxgb_extension_ioctl; 696167514Skmacy 697167514Skmacy pi->port_cdev = make_dev(cxgb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 698167514Skmacy pi->ifp->if_xname); 699167514Skmacy 700167514Skmacy if (pi->port_cdev == NULL) 701167514Skmacy return (ENOMEM); 702167514Skmacy 703167514Skmacy pi->port_cdev->si_drv1 = (void *)pi; 704167514Skmacy 705167514Skmacy return (0); 706167514Skmacy} 707167514Skmacy 708167514Skmacy 709167514Skmacy#ifdef TSO_SUPPORTED 710167514Skmacy#define CXGB_CAP (IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM | IFCAP_TSO | IFCAP_JUMBO_MTU) 711167514Skmacy/* Don't enable TSO6 yet */ 712167514Skmacy#define CXGB_CAP_ENABLE (IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM | IFCAP_TSO4 | IFCAP_JUMBO_MTU) 713167514Skmacy#else 714167514Skmacy#define CXGB_CAP (IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_JUMBO_MTU) 715167514Skmacy/* Don't enable TSO6 yet */ 716167514Skmacy#define CXGB_CAP_ENABLE (IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_JUMBO_MTU) 717167514Skmacy#define IFCAP_TSO4 0x0 718167514Skmacy#define CSUM_TSO 0x0 719167514Skmacy#endif 720167514Skmacy 721167514Skmacy 722167514Skmacystatic int 723167514Skmacycxgb_port_attach(device_t dev) 724167514Skmacy{ 725167514Skmacy struct port_info *p; 726167514Skmacy struct ifnet *ifp; 727167514Skmacy int media_flags; 728167514Skmacy int err; 729167514Skmacy char buf[64]; 730167514Skmacy 731167514Skmacy p = device_get_softc(dev); 732167514Skmacy 733167514Skmacy snprintf(buf, sizeof(buf), "cxgb port %d", p->port); 734167514Skmacy mtx_init(&p->lock, buf, 0, MTX_DEF); 735167514Skmacy 736167514Skmacy /* Allocate an ifnet object and set it up */ 737167514Skmacy ifp = p->ifp = if_alloc(IFT_ETHER); 738167514Skmacy if (ifp == NULL) { 739167514Skmacy device_printf(dev, "Cannot allocate ifnet\n"); 740167514Skmacy return (ENOMEM); 741167514Skmacy } 742167514Skmacy 743167514Skmacy /* 744167514Skmacy * Note that there is currently no watchdog timer. 745167514Skmacy */ 746167514Skmacy if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 747167514Skmacy ifp->if_init = cxgb_init; 748167514Skmacy ifp->if_softc = p; 749167514Skmacy ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 750167514Skmacy ifp->if_ioctl = cxgb_ioctl; 751167514Skmacy ifp->if_start = cxgb_start; 752167514Skmacy ifp->if_timer = 0; /* Disable ifnet watchdog */ 753167514Skmacy ifp->if_watchdog = NULL; 754167514Skmacy 755167514Skmacy ifp->if_snd.ifq_drv_maxlen = TX_ETH_Q_SIZE; 756167514Skmacy IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); 757167514Skmacy IFQ_SET_READY(&ifp->if_snd); 758167514Skmacy 759167514Skmacy ifp->if_hwassist = ifp->if_capabilities = ifp->if_capenable = 0; 760167514Skmacy ifp->if_capabilities |= CXGB_CAP; 761167514Skmacy ifp->if_capenable |= CXGB_CAP_ENABLE; 762167514Skmacy ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP | CSUM_IP | CSUM_TSO); 763167514Skmacy ifp->if_baudrate = 100000000; 764167514Skmacy 765167514Skmacy ether_ifattach(ifp, p->hw_addr); 766167514Skmacy#ifdef DEFAULT_JUMBO 767167514Skmacy ifp->if_mtu = 9000; 768167514Skmacy#endif 769167514Skmacy if ((err = cxgb_makedev(p)) != 0) { 770167514Skmacy printf("makedev failed %d\n", err); 771167514Skmacy return (err); 772167514Skmacy } 773167514Skmacy ifmedia_init(&p->media, IFM_IMASK, cxgb_media_change, 774167514Skmacy cxgb_media_status); 775167514Skmacy 776167514Skmacy if (!strcmp(p->port_type->desc, "10GBASE-CX4")) 777167514Skmacy media_flags = IFM_ETHER | IFM_10G_CX4; 778167514Skmacy else if (!strcmp(p->port_type->desc, "10GBASE-SR")) 779167514Skmacy media_flags = IFM_ETHER | IFM_10G_SR; 780167514Skmacy else if (!strcmp(p->port_type->desc, "10GBASE-XR")) 781167514Skmacy media_flags = IFM_ETHER | IFM_10G_LR; 782167514Skmacy else { 783167514Skmacy printf("unsupported media type %s\n", p->port_type->desc); 784167514Skmacy return (ENXIO); 785167514Skmacy } 786167514Skmacy 787167514Skmacy ifmedia_add(&p->media, media_flags, 0, NULL); 788167514Skmacy ifmedia_add(&p->media, IFM_ETHER | IFM_AUTO, 0, NULL); 789167514Skmacy ifmedia_set(&p->media, media_flags); 790167514Skmacy 791167514Skmacy snprintf(buf, sizeof(buf), "cxgb_port_taskq%d", p->port); 792167514Skmacy#ifdef TASKQUEUE_CURRENT 793167514Skmacy /* Create a port for handling TX without starvation */ 794167514Skmacy p->tq = taskqueue_create(buf, M_NOWAIT, 795167514Skmacy taskqueue_thread_enqueue, &p->tq); 796167514Skmacy#else 797167514Skmacy /* Create a port for handling TX without starvation */ 798167514Skmacy p->tq = taskqueue_create_fast(buf, M_NOWAIT, 799167514Skmacy taskqueue_thread_enqueue, &p->tq); 800167514Skmacy#endif 801167514Skmacy 802167514Skmacy 803167514Skmacy if (p->tq == NULL) { 804167514Skmacy device_printf(dev, "failed to allocate port task queue\n"); 805167514Skmacy return (ENOMEM); 806167514Skmacy } 807167514Skmacy taskqueue_start_threads(&p->tq, 1, PI_NET, "%s taskq", 808167514Skmacy device_get_nameunit(dev)); 809167514Skmacy TASK_INIT(&p->start_task, 0, cxgb_start_proc, ifp); 810167514Skmacy 811167514Skmacy 812167514Skmacy return (0); 813167514Skmacy} 814167514Skmacy 815167514Skmacystatic int 816167514Skmacycxgb_port_detach(device_t dev) 817167514Skmacy{ 818167514Skmacy struct port_info *p; 819167514Skmacy 820167514Skmacy p = device_get_softc(dev); 821167514Skmacy mtx_destroy(&p->lock); 822167514Skmacy if (p->tq != NULL) { 823167514Skmacy taskqueue_drain(p->tq, &p->start_task); 824167514Skmacy taskqueue_free(p->tq); 825167514Skmacy p->tq = NULL; 826167514Skmacy } 827167514Skmacy 828167514Skmacy ether_ifdetach(p->ifp); 829167514Skmacy if_free(p->ifp); 830167514Skmacy 831167514Skmacy destroy_dev(p->port_cdev); 832167514Skmacy 833167514Skmacy 834167514Skmacy return (0); 835167514Skmacy} 836167514Skmacy 837167514Skmacyvoid 838167514Skmacyt3_fatal_err(struct adapter *sc) 839167514Skmacy{ 840167514Skmacy u_int fw_status[4]; 841167514Skmacy 842167514Skmacy device_printf(sc->dev,"encountered fatal error, operation suspended\n"); 843167514Skmacy if (!t3_cim_ctl_blk_read(sc, 0xa0, 4, fw_status)) 844167514Skmacy device_printf(sc->dev, "FW_ status: 0x%x, 0x%x, 0x%x, 0x%x\n", 845167514Skmacy fw_status[0], fw_status[1], fw_status[2], fw_status[3]); 846167514Skmacy} 847167514Skmacy 848167514Skmacyint 849167514Skmacyt3_os_find_pci_capability(adapter_t *sc, int cap) 850167514Skmacy{ 851167514Skmacy device_t dev; 852167514Skmacy struct pci_devinfo *dinfo; 853167514Skmacy pcicfgregs *cfg; 854167514Skmacy uint32_t status; 855167514Skmacy uint8_t ptr; 856167514Skmacy 857167514Skmacy dev = sc->dev; 858167514Skmacy dinfo = device_get_ivars(dev); 859167514Skmacy cfg = &dinfo->cfg; 860167514Skmacy 861167514Skmacy status = pci_read_config(dev, PCIR_STATUS, 2); 862167514Skmacy if (!(status & PCIM_STATUS_CAPPRESENT)) 863167514Skmacy return (0); 864167514Skmacy 865167514Skmacy switch (cfg->hdrtype & PCIM_HDRTYPE) { 866167514Skmacy case 0: 867167514Skmacy case 1: 868167514Skmacy ptr = PCIR_CAP_PTR; 869167514Skmacy break; 870167514Skmacy case 2: 871167514Skmacy ptr = PCIR_CAP_PTR_2; 872167514Skmacy break; 873167514Skmacy default: 874167514Skmacy return (0); 875167514Skmacy break; 876167514Skmacy } 877167514Skmacy ptr = pci_read_config(dev, ptr, 1); 878167514Skmacy 879167514Skmacy while (ptr != 0) { 880167514Skmacy if (pci_read_config(dev, ptr + PCICAP_ID, 1) == cap) 881167514Skmacy return (ptr); 882167514Skmacy ptr = pci_read_config(dev, ptr + PCICAP_NEXTPTR, 1); 883167514Skmacy } 884167514Skmacy 885167514Skmacy return (0); 886167514Skmacy} 887167514Skmacy 888167514Skmacyint 889167514Skmacyt3_os_pci_save_state(struct adapter *sc) 890167514Skmacy{ 891167514Skmacy device_t dev; 892167514Skmacy struct pci_devinfo *dinfo; 893167514Skmacy 894167514Skmacy dev = sc->dev; 895167514Skmacy dinfo = device_get_ivars(dev); 896167514Skmacy 897167514Skmacy pci_cfg_save(dev, dinfo, 0); 898167514Skmacy return (0); 899167514Skmacy} 900167514Skmacy 901167514Skmacyint 902167514Skmacyt3_os_pci_restore_state(struct adapter *sc) 903167514Skmacy{ 904167514Skmacy device_t dev; 905167514Skmacy struct pci_devinfo *dinfo; 906167514Skmacy 907167514Skmacy dev = sc->dev; 908167514Skmacy dinfo = device_get_ivars(dev); 909167514Skmacy 910167514Skmacy pci_cfg_restore(dev, dinfo); 911167514Skmacy return (0); 912167514Skmacy} 913167514Skmacy 914167514Skmacy/** 915167514Skmacy * t3_os_link_changed - handle link status changes 916167514Skmacy * @adapter: the adapter associated with the link change 917167514Skmacy * @port_id: the port index whose limk status has changed 918167514Skmacy * @link_stat: the new status of the link 919167514Skmacy * @speed: the new speed setting 920167514Skmacy * @duplex: the new duplex setting 921167514Skmacy * @fc: the new flow-control setting 922167514Skmacy * 923167514Skmacy * This is the OS-dependent handler for link status changes. The OS 924167514Skmacy * neutral handler takes care of most of the processing for these events, 925167514Skmacy * then calls this handler for any OS-specific processing. 926167514Skmacy */ 927167514Skmacyvoid 928167514Skmacyt3_os_link_changed(adapter_t *adapter, int port_id, int link_status, int speed, 929167514Skmacy int duplex, int fc) 930167514Skmacy{ 931167514Skmacy struct port_info *pi = &adapter->port[port_id]; 932167514Skmacy 933167514Skmacy if ((pi->ifp->if_flags & IFF_UP) == 0) 934167514Skmacy return; 935167514Skmacy 936167514Skmacy if (link_status) 937167514Skmacy if_link_state_change(pi->ifp, LINK_STATE_UP); 938167514Skmacy else 939167514Skmacy if_link_state_change(pi->ifp, LINK_STATE_DOWN); 940167514Skmacy 941167514Skmacy} 942167514Skmacy 943167514Skmacy 944167514Skmacy/* 945167514Skmacy * Interrupt-context handler for external (PHY) interrupts. 946167514Skmacy */ 947167514Skmacyvoid 948167514Skmacyt3_os_ext_intr_handler(adapter_t *sc) 949167514Skmacy{ 950167514Skmacy if (cxgb_debug) 951167514Skmacy printf("t3_os_ext_intr_handler\n"); 952167514Skmacy /* 953167514Skmacy * Schedule a task to handle external interrupts as they may be slow 954167514Skmacy * and we use a mutex to protect MDIO registers. We disable PHY 955167514Skmacy * interrupts in the meantime and let the task reenable them when 956167514Skmacy * it's done. 957167514Skmacy */ 958167514Skmacy if (sc->slow_intr_mask) { 959167514Skmacy sc->slow_intr_mask &= ~F_T3DBG; 960167514Skmacy t3_write_reg(sc, A_PL_INT_ENABLE0, sc->slow_intr_mask); 961167514Skmacy taskqueue_enqueue(sc->tq, &sc->ext_intr_task); 962167514Skmacy } 963167514Skmacy} 964167514Skmacy 965167514Skmacyvoid 966167514Skmacyt3_os_set_hw_addr(adapter_t *adapter, int port_idx, u8 hw_addr[]) 967167514Skmacy{ 968167514Skmacy 969167514Skmacy /* 970167514Skmacy * The ifnet might not be allocated before this gets called, 971167514Skmacy * as this is called early on in attach by t3_prep_adapter 972167514Skmacy * save the address off in the port structure 973167514Skmacy */ 974167514Skmacy if (cxgb_debug) 975167514Skmacy printf("set_hw_addr on idx %d addr %6D\n", port_idx, hw_addr, ":"); 976167514Skmacy bcopy(hw_addr, adapter->port[port_idx].hw_addr, ETHER_ADDR_LEN); 977167514Skmacy} 978167514Skmacy 979167514Skmacy/** 980167514Skmacy * link_start - enable a port 981167514Skmacy * @p: the port to enable 982167514Skmacy * 983167514Skmacy * Performs the MAC and PHY actions needed to enable a port. 984167514Skmacy */ 985167514Skmacystatic void 986167514Skmacycxgb_link_start(struct port_info *p) 987167514Skmacy{ 988167514Skmacy struct ifnet *ifp; 989167514Skmacy struct t3_rx_mode rm; 990167514Skmacy struct cmac *mac = &p->mac; 991167514Skmacy 992167514Skmacy ifp = p->ifp; 993167514Skmacy 994167514Skmacy t3_init_rx_mode(&rm, p); 995167514Skmacy t3_mac_reset(mac); 996167514Skmacy t3_mac_set_mtu(mac, ifp->if_mtu); 997167514Skmacy t3_mac_set_address(mac, 0, p->hw_addr); 998167514Skmacy t3_mac_set_rx_mode(mac, &rm); 999167514Skmacy t3_link_start(&p->phy, mac, &p->link_config); 1000167514Skmacy t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 1001167514Skmacy} 1002167514Skmacy 1003167514Skmacy/** 1004167514Skmacy * setup_rss - configure Receive Side Steering (per-queue connection demux) 1005167514Skmacy * @adap: the adapter 1006167514Skmacy * 1007167514Skmacy * Sets up RSS to distribute packets to multiple receive queues. We 1008167514Skmacy * configure the RSS CPU lookup table to distribute to the number of HW 1009167514Skmacy * receive queues, and the response queue lookup table to narrow that 1010167514Skmacy * down to the response queues actually configured for each port. 1011167514Skmacy * We always configure the RSS mapping for two ports since the mapping 1012167514Skmacy * table has plenty of entries. 1013167514Skmacy */ 1014167514Skmacystatic void 1015167514Skmacysetup_rss(adapter_t *adap) 1016167514Skmacy{ 1017167514Skmacy int i; 1018167514Skmacy u_int nq0 = adap->port[0].nqsets; 1019167514Skmacy u_int nq1 = max((u_int)adap->port[1].nqsets, 1U); 1020167514Skmacy uint8_t cpus[SGE_QSETS + 1]; 1021167514Skmacy uint16_t rspq_map[RSS_TABLE_SIZE]; 1022167514Skmacy 1023167514Skmacy for (i = 0; i < SGE_QSETS; ++i) 1024167514Skmacy cpus[i] = i; 1025167514Skmacy cpus[SGE_QSETS] = 0xff; 1026167514Skmacy 1027167514Skmacy for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) { 1028167514Skmacy rspq_map[i] = i % nq0; 1029167514Skmacy rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; 1030167514Skmacy } 1031167514Skmacy 1032167514Skmacy t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | 1033167514Skmacy F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | 1034167514Skmacy V_RRCPLCPUSIZE(6), cpus, rspq_map); 1035167514Skmacy} 1036167514Skmacy 1037167514Skmacystatic void 1038167514Skmacysend_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, 1039167514Skmacy int hi, int port) 1040167514Skmacy{ 1041167514Skmacy struct mbuf *m; 1042167514Skmacy struct mngt_pktsched_wr *req; 1043167514Skmacy 1044167514Skmacy m = m_gethdr(M_NOWAIT, MT_DATA); 1045167514Skmacy req = (struct mngt_pktsched_wr *)m->m_data; 1046167514Skmacy req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT)); 1047167514Skmacy req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET; 1048167514Skmacy req->sched = sched; 1049167514Skmacy req->idx = qidx; 1050167514Skmacy req->min = lo; 1051167514Skmacy req->max = hi; 1052167514Skmacy req->binding = port; 1053167514Skmacy m->m_len = m->m_pkthdr.len = sizeof(*req); 1054167514Skmacy t3_mgmt_tx(adap, m); 1055167514Skmacy} 1056167514Skmacy 1057167514Skmacystatic void 1058167514Skmacybind_qsets(adapter_t *sc) 1059167514Skmacy{ 1060167514Skmacy int i, j; 1061167514Skmacy 1062167514Skmacy for (i = 0; i < (sc)->params.nports; ++i) { 1063167514Skmacy const struct port_info *pi = adap2pinfo(sc, i); 1064167514Skmacy 1065167514Skmacy for (j = 0; j < pi->nqsets; ++j) 1066167514Skmacy send_pktsched_cmd(sc, 1, pi->first_qset + j, -1, 1067167514Skmacy -1, i); 1068167514Skmacy } 1069167514Skmacy} 1070167514Skmacy 1071167514Skmacystatic void 1072167514Skmacycxgb_init(void *arg) 1073167514Skmacy{ 1074167514Skmacy struct port_info *p = arg; 1075167514Skmacy 1076167514Skmacy PORT_LOCK(p); 1077167514Skmacy cxgb_init_locked(p); 1078167514Skmacy PORT_UNLOCK(p); 1079167514Skmacy} 1080167514Skmacy 1081167514Skmacystatic void 1082167514Skmacycxgb_init_locked(struct port_info *p) 1083167514Skmacy{ 1084167514Skmacy struct ifnet *ifp; 1085167514Skmacy adapter_t *sc = p->adapter; 1086167514Skmacy int error; 1087167514Skmacy 1088167514Skmacy mtx_assert(&p->lock, MA_OWNED); 1089167514Skmacy 1090167514Skmacy ifp = p->ifp; 1091167514Skmacy if ((sc->flags & FW_UPTODATE) == 0) { 1092167514Skmacy device_printf(sc->dev, "updating firmware to version %d.%d\n", 1093167514Skmacy CHELSIO_FW_MAJOR, CHELSIO_FW_MINOR); 1094167514Skmacy if ((error = cxgb_fw_download(sc, sc->dev)) != 0) { 1095167514Skmacy device_printf(sc->dev, "firmware download failed err: %d" 1096167514Skmacy "interface will be unavailable\n", error); 1097167514Skmacy return; 1098167514Skmacy } 1099167514Skmacy sc->flags |= FW_UPTODATE; 1100167514Skmacy } 1101167514Skmacy 1102167514Skmacy cxgb_link_start(p); 1103167514Skmacy ADAPTER_LOCK(p->adapter); 1104167514Skmacy if (p->adapter->open_device_map == 0) 1105167514Skmacy t3_intr_clear(sc); 1106167514Skmacy t3_sge_start(sc); 1107167514Skmacy 1108167514Skmacy p->adapter->open_device_map |= (1 << p->port); 1109167514Skmacy ADAPTER_UNLOCK(p->adapter); 1110167514Skmacy t3_intr_enable(sc); 1111167514Skmacy t3_port_intr_enable(sc, p->port); 1112167514Skmacy if ((p->adapter->flags & (USING_MSIX | QUEUES_BOUND)) == USING_MSIX) 1113167514Skmacy bind_qsets(sc); 1114167514Skmacy p->adapter->flags |= QUEUES_BOUND; 1115167514Skmacy callout_reset(&sc->cxgb_tick_ch, sc->params.stats_update_period * hz, 1116167514Skmacy cxgb_tick, sc); 1117167514Skmacy 1118167514Skmacy 1119167514Skmacy ifp->if_drv_flags |= IFF_DRV_RUNNING; 1120167514Skmacy ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1121167514Skmacy} 1122167514Skmacy 1123167514Skmacystatic void 1124167514Skmacycxgb_set_rxmode(struct port_info *p) 1125167514Skmacy{ 1126167514Skmacy struct t3_rx_mode rm; 1127167514Skmacy struct cmac *mac = &p->mac; 1128167514Skmacy 1129167514Skmacy t3_init_rx_mode(&rm, p); 1130167514Skmacy t3_mac_set_rx_mode(mac, &rm); 1131167514Skmacy} 1132167514Skmacy 1133167514Skmacystatic void 1134167514Skmacycxgb_stop(struct port_info *p) 1135167514Skmacy{ 1136167514Skmacy struct ifnet *ifp; 1137167514Skmacy 1138167514Skmacy callout_drain(&p->adapter->cxgb_tick_ch); 1139167514Skmacy ifp = p->ifp; 1140167514Skmacy 1141167514Skmacy PORT_LOCK(p); 1142167514Skmacy ADAPTER_LOCK(p->adapter); 1143167514Skmacy ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 1144167514Skmacy p->adapter->open_device_map &= ~(1 << p->port); 1145167514Skmacy if (p->adapter->open_device_map == 0) 1146167514Skmacy t3_intr_disable(p->adapter); 1147167514Skmacy ADAPTER_UNLOCK(p->adapter); 1148167514Skmacy t3_port_intr_disable(p->adapter, p->port); 1149167514Skmacy t3_mac_disable(&p->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); 1150167514Skmacy PORT_UNLOCK(p); 1151167514Skmacy 1152167514Skmacy} 1153167514Skmacy 1154167514Skmacystatic int 1155167514Skmacycxgb_ioctl(struct ifnet *ifp, unsigned long command, caddr_t data) 1156167514Skmacy{ 1157167514Skmacy struct port_info *p = ifp->if_softc; 1158167514Skmacy struct ifaddr *ifa = (struct ifaddr *)data; 1159167514Skmacy struct ifreq *ifr = (struct ifreq *)data; 1160167514Skmacy int flags, error = 0; 1161167514Skmacy uint32_t mask; 1162167514Skmacy 1163167514Skmacy switch (command) { 1164167514Skmacy case SIOCSIFMTU: 1165167514Skmacy if ((ifr->ifr_mtu < ETHERMIN) || 1166167514Skmacy (ifr->ifr_mtu > ETHER_MAX_LEN_JUMBO)) 1167167514Skmacy error = EINVAL; 1168167514Skmacy else if (ifp->if_mtu != ifr->ifr_mtu) { 1169167514Skmacy PORT_LOCK(p); 1170167514Skmacy ifp->if_mtu = ifr->ifr_mtu; 1171167514Skmacy t3_mac_set_mtu(&p->mac, ifp->if_mtu); 1172167514Skmacy PORT_UNLOCK(p); 1173167514Skmacy } 1174167514Skmacy break; 1175167514Skmacy case SIOCSIFADDR: 1176167514Skmacy case SIOCGIFADDR: 1177167514Skmacy if (ifa->ifa_addr->sa_family == AF_INET) { 1178167514Skmacy ifp->if_flags |= IFF_UP; 1179167514Skmacy if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1180167514Skmacy cxgb_init(p); 1181167514Skmacy } 1182167514Skmacy arp_ifinit(ifp, ifa); 1183167514Skmacy } else 1184167514Skmacy error = ether_ioctl(ifp, command, data); 1185167514Skmacy break; 1186167514Skmacy case SIOCSIFFLAGS: 1187167514Skmacy PORT_LOCK(p); 1188167514Skmacy if (ifp->if_flags & IFF_UP) { 1189167514Skmacy if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1190167514Skmacy flags = p->if_flags; 1191167514Skmacy if (((ifp->if_flags ^ flags) & IFF_PROMISC) || 1192167514Skmacy ((ifp->if_flags ^ flags) & IFF_ALLMULTI)) 1193167514Skmacy cxgb_set_rxmode(p); 1194167514Skmacy 1195167514Skmacy } else 1196167514Skmacy cxgb_init_locked(p); 1197167514Skmacy } else { 1198167514Skmacy if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1199167514Skmacy cxgb_stop(p); 1200167514Skmacy } 1201167514Skmacy } 1202167514Skmacy p->if_flags = ifp->if_flags; 1203167514Skmacy PORT_UNLOCK(p); 1204167514Skmacy break; 1205167514Skmacy case SIOCSIFMEDIA: 1206167514Skmacy case SIOCGIFMEDIA: 1207167514Skmacy error = ifmedia_ioctl(ifp, ifr, &p->media, command); 1208167514Skmacy break; 1209167514Skmacy case SIOCSIFCAP: 1210167514Skmacy PORT_LOCK(p); 1211167514Skmacy mask = ifr->ifr_reqcap ^ ifp->if_capenable; 1212167514Skmacy if (mask & IFCAP_TXCSUM) { 1213167514Skmacy if (IFCAP_TXCSUM & ifp->if_capenable) { 1214167514Skmacy ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 1215167514Skmacy ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 1216167514Skmacy | CSUM_TSO); 1217167514Skmacy } else { 1218167514Skmacy ifp->if_capenable |= IFCAP_TXCSUM; 1219167514Skmacy ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 1220167514Skmacy } 1221167514Skmacy } else if (mask & IFCAP_RXCSUM) { 1222167514Skmacy if (IFCAP_RXCSUM & ifp->if_capenable) { 1223167514Skmacy ifp->if_capenable &= ~IFCAP_RXCSUM; 1224167514Skmacy } else { 1225167514Skmacy ifp->if_capenable |= IFCAP_RXCSUM; 1226167514Skmacy } 1227167514Skmacy } 1228167514Skmacy if (mask & IFCAP_TSO4) { 1229167514Skmacy if (IFCAP_TSO4 & ifp->if_capenable) { 1230167514Skmacy ifp->if_capenable &= ~IFCAP_TSO4; 1231167514Skmacy ifp->if_hwassist &= ~CSUM_TSO; 1232167514Skmacy } else if (IFCAP_TXCSUM & ifp->if_capenable) { 1233167514Skmacy ifp->if_capenable |= IFCAP_TSO4; 1234167514Skmacy ifp->if_hwassist |= CSUM_TSO; 1235167514Skmacy } else { 1236167514Skmacy if (cxgb_debug) 1237167514Skmacy printf("cxgb requires tx checksum offload" 1238167514Skmacy " be enabled to use TSO\n"); 1239167514Skmacy error = EINVAL; 1240167514Skmacy } 1241167514Skmacy } 1242167514Skmacy PORT_UNLOCK(p); 1243167514Skmacy break; 1244167514Skmacy default: 1245167514Skmacy error = ether_ioctl(ifp, command, data); 1246167514Skmacy break; 1247167514Skmacy } 1248167514Skmacy 1249167514Skmacy return (error); 1250167514Skmacy} 1251167514Skmacy 1252167514Skmacystatic int 1253167514Skmacycxgb_start_tx(struct ifnet *ifp, uint32_t txmax) 1254167514Skmacy{ 1255167514Skmacy struct sge_qset *qs; 1256167514Skmacy struct sge_txq *txq; 1257167514Skmacy struct port_info *p = ifp->if_softc; 1258167514Skmacy struct mbuf *m = NULL; 1259167514Skmacy int err, in_use_init; 1260167514Skmacy 1261167514Skmacy 1262167514Skmacy if (!p->link_config.link_ok) 1263167514Skmacy return (ENXIO); 1264167514Skmacy 1265167514Skmacy if (IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1266167514Skmacy return (ENOBUFS); 1267167514Skmacy 1268167514Skmacy qs = &p->adapter->sge.qs[p->first_qset]; 1269167514Skmacy txq = &qs->txq[TXQ_ETH]; 1270167514Skmacy err = 0; 1271167514Skmacy 1272167514Skmacy mtx_lock(&txq->lock); 1273167514Skmacy in_use_init = txq->in_use; 1274167514Skmacy while ((txq->in_use - in_use_init < txmax) && 1275167514Skmacy (txq->size > txq->in_use + TX_MAX_DESC)) { 1276167514Skmacy IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1277167514Skmacy if (m == NULL) 1278167514Skmacy break; 1279167514Skmacy if ((err = t3_encap(p, &m)) != 0) 1280167514Skmacy break; 1281167514Skmacy BPF_MTAP(ifp, m); 1282167514Skmacy } 1283167514Skmacy mtx_unlock(&txq->lock); 1284167514Skmacy 1285167514Skmacy if (__predict_false(err)) { 1286167514Skmacy if (cxgb_debug) 1287167514Skmacy printf("would set OFLAGS\n"); 1288167514Skmacy if (err == ENOMEM) { 1289167514Skmacy IFQ_LOCK(&ifp->if_snd); 1290167514Skmacy IFQ_DRV_PREPEND(&ifp->if_snd, m); 1291167514Skmacy IFQ_UNLOCK(&ifp->if_snd); 1292167514Skmacy } 1293167514Skmacy } 1294167514Skmacy if (err == 0 && m == NULL) 1295167514Skmacy err = ENOBUFS; 1296167514Skmacy 1297167514Skmacy return (err); 1298167514Skmacy} 1299167514Skmacy 1300167514Skmacystatic void 1301167514Skmacycxgb_start_proc(void *arg, int ncount) 1302167514Skmacy{ 1303167514Skmacy struct ifnet *ifp = arg; 1304167514Skmacy struct port_info *pi = ifp->if_softc; 1305167514Skmacy struct sge_qset *qs; 1306167514Skmacy struct sge_txq *txq; 1307167514Skmacy int error = 0; 1308167514Skmacy 1309167514Skmacy qs = &pi->adapter->sge.qs[pi->first_qset]; 1310167514Skmacy txq = &qs->txq[TXQ_ETH]; 1311167514Skmacy 1312167514Skmacy while (error == 0) { 1313167514Skmacy if (desc_reclaimable(txq) > TX_START_MAX_DESC) 1314167514Skmacy taskqueue_enqueue(pi->adapter->tq, &pi->adapter->timer_reclaim_task); 1315167514Skmacy 1316167514Skmacy error = cxgb_start_tx(ifp, TX_MAX_DESC + 1); 1317167514Skmacy } 1318167514Skmacy} 1319167514Skmacy 1320167514Skmacystatic void 1321167514Skmacycxgb_start(struct ifnet *ifp) 1322167514Skmacy{ 1323167514Skmacy struct port_info *pi = ifp->if_softc; 1324167514Skmacy struct sge_qset *qs; 1325167514Skmacy struct sge_txq *txq; 1326167514Skmacy int err; 1327167514Skmacy 1328167514Skmacy qs = &pi->adapter->sge.qs[pi->first_qset]; 1329167514Skmacy txq = &qs->txq[TXQ_ETH]; 1330167514Skmacy 1331167514Skmacy if (desc_reclaimable(txq) > TX_START_MAX_DESC) 1332167514Skmacy taskqueue_enqueue(pi->adapter->tq, &pi->adapter->timer_reclaim_task); 1333167514Skmacy 1334167514Skmacy err = cxgb_start_tx(ifp, TX_START_MAX_DESC); 1335167514Skmacy 1336167514Skmacy if (err == 0) 1337167514Skmacy taskqueue_enqueue(pi->tq, &pi->start_task); 1338167514Skmacy} 1339167514Skmacy 1340167514Skmacy 1341167514Skmacystatic int 1342167514Skmacycxgb_media_change(struct ifnet *ifp) 1343167514Skmacy{ 1344167514Skmacy if_printf(ifp, "media change not supported\n"); 1345167514Skmacy return (ENXIO); 1346167514Skmacy} 1347167514Skmacy 1348167514Skmacystatic void 1349167514Skmacycxgb_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 1350167514Skmacy{ 1351167514Skmacy struct port_info *p = ifp->if_softc; 1352167514Skmacy 1353167514Skmacy ifmr->ifm_status = IFM_AVALID; 1354167514Skmacy ifmr->ifm_active = IFM_ETHER; 1355167514Skmacy 1356167514Skmacy if (!p->link_config.link_ok) 1357167514Skmacy return; 1358167514Skmacy 1359167514Skmacy ifmr->ifm_status |= IFM_ACTIVE; 1360167514Skmacy 1361167514Skmacy if (p->link_config.duplex) 1362167514Skmacy ifmr->ifm_active |= IFM_FDX; 1363167514Skmacy else 1364167514Skmacy ifmr->ifm_active |= IFM_HDX; 1365167514Skmacy} 1366167514Skmacy 1367167514Skmacystatic void 1368167514Skmacycxgb_async_intr(void *data) 1369167514Skmacy{ 1370167514Skmacy if (cxgb_debug) 1371167514Skmacy printf("cxgb_async_intr\n"); 1372167514Skmacy} 1373167514Skmacy 1374167514Skmacystatic void 1375167514Skmacycxgb_ext_intr_handler(void *arg, int count) 1376167514Skmacy{ 1377167514Skmacy adapter_t *sc = (adapter_t *)arg; 1378167514Skmacy 1379167514Skmacy if (cxgb_debug) 1380167514Skmacy printf("cxgb_ext_intr_handler\n"); 1381167514Skmacy 1382167514Skmacy t3_phy_intr_handler(sc); 1383167514Skmacy 1384167514Skmacy /* Now reenable external interrupts */ 1385167514Skmacy if (sc->slow_intr_mask) { 1386167514Skmacy sc->slow_intr_mask |= F_T3DBG; 1387167514Skmacy t3_write_reg(sc, A_PL_INT_CAUSE0, F_T3DBG); 1388167514Skmacy t3_write_reg(sc, A_PL_INT_ENABLE0, sc->slow_intr_mask); 1389167514Skmacy } 1390167514Skmacy} 1391167514Skmacy 1392167514Skmacystatic void 1393167514Skmacycxgb_tick(void *arg) 1394167514Skmacy{ 1395167514Skmacy adapter_t *sc = (adapter_t *)arg; 1396167514Skmacy const struct adapter_params *p = &sc->params; 1397167514Skmacy 1398167514Skmacy if (p->linkpoll_period) 1399167514Skmacy check_link_status(sc); 1400167514Skmacy 1401167514Skmacy callout_reset(&sc->cxgb_tick_ch, sc->params.stats_update_period * hz, 1402167514Skmacy cxgb_tick, sc); 1403167514Skmacy} 1404167514Skmacy 1405167514Skmacystatic void 1406167514Skmacycheck_link_status(adapter_t *sc) 1407167514Skmacy{ 1408167514Skmacy int i; 1409167514Skmacy 1410167514Skmacy for (i = 0; i < (sc)->params.nports; ++i) { 1411167514Skmacy struct port_info *p = &sc->port[i]; 1412167514Skmacy 1413167514Skmacy if (!(p->port_type->caps & SUPPORTED_IRQ)) 1414167514Skmacy t3_link_changed(sc, i); 1415167514Skmacy } 1416167514Skmacy} 1417167514Skmacy 1418167514Skmacystatic int 1419167514Skmacyin_range(int val, int lo, int hi) 1420167514Skmacy{ 1421167514Skmacy return val < 0 || (val <= hi && val >= lo); 1422167514Skmacy} 1423167514Skmacy 1424167514Skmacystatic int 1425167514Skmacycxgb_extension_ioctl(struct cdev *dev, unsigned long cmd, caddr_t data, 1426167514Skmacy int fflag, struct thread *td) 1427167514Skmacy{ 1428167514Skmacy int mmd, error = 0; 1429167514Skmacy struct port_info *pi = dev->si_drv1; 1430167514Skmacy adapter_t *sc = pi->adapter; 1431167514Skmacy 1432167514Skmacy#ifdef PRIV_SUPPORTED 1433167514Skmacy if (priv_check(td, PRIV_DRIVER)) { 1434167514Skmacy if (cxgb_debug) 1435167514Skmacy printf("user does not have access to privileged ioctls\n"); 1436167514Skmacy return (EPERM); 1437167514Skmacy } 1438167514Skmacy#else 1439167514Skmacy if (suser(td)) { 1440167514Skmacy if (cxgb_debug) 1441167514Skmacy printf("user does not have access to privileged ioctls\n"); 1442167514Skmacy return (EPERM); 1443167514Skmacy } 1444167514Skmacy#endif 1445167514Skmacy 1446167514Skmacy switch (cmd) { 1447167514Skmacy case SIOCGMIIREG: { 1448167514Skmacy uint32_t val; 1449167514Skmacy struct cphy *phy = &pi->phy; 1450167514Skmacy struct mii_data *mid = (struct mii_data *)data; 1451167514Skmacy 1452167514Skmacy if (!phy->mdio_read) 1453167514Skmacy return (EOPNOTSUPP); 1454167514Skmacy if (is_10G(sc)) { 1455167514Skmacy mmd = mid->phy_id >> 8; 1456167514Skmacy if (!mmd) 1457167514Skmacy mmd = MDIO_DEV_PCS; 1458167514Skmacy else if (mmd > MDIO_DEV_XGXS) 1459167514Skmacy return -EINVAL; 1460167514Skmacy 1461167514Skmacy error = phy->mdio_read(sc, mid->phy_id & 0x1f, mmd, 1462167514Skmacy mid->reg_num, &val); 1463167514Skmacy } else 1464167514Skmacy error = phy->mdio_read(sc, mid->phy_id & 0x1f, 0, 1465167514Skmacy mid->reg_num & 0x1f, &val); 1466167514Skmacy if (error == 0) 1467167514Skmacy mid->val_out = val; 1468167514Skmacy break; 1469167514Skmacy } 1470167514Skmacy case SIOCSMIIREG: { 1471167514Skmacy struct cphy *phy = &pi->phy; 1472167514Skmacy struct mii_data *mid = (struct mii_data *)data; 1473167514Skmacy 1474167514Skmacy if (!phy->mdio_write) 1475167514Skmacy return (EOPNOTSUPP); 1476167514Skmacy if (is_10G(sc)) { 1477167514Skmacy mmd = mid->phy_id >> 8; 1478167514Skmacy if (!mmd) 1479167514Skmacy mmd = MDIO_DEV_PCS; 1480167514Skmacy else if (mmd > MDIO_DEV_XGXS) 1481167514Skmacy return (EINVAL); 1482167514Skmacy 1483167514Skmacy error = phy->mdio_write(sc, mid->phy_id & 0x1f, 1484167514Skmacy mmd, mid->reg_num, mid->val_in); 1485167514Skmacy } else 1486167514Skmacy error = phy->mdio_write(sc, mid->phy_id & 0x1f, 0, 1487167514Skmacy mid->reg_num & 0x1f, 1488167514Skmacy mid->val_in); 1489167514Skmacy break; 1490167514Skmacy } 1491167514Skmacy case CHELSIO_SETREG: { 1492167514Skmacy struct ch_reg *edata = (struct ch_reg *)data; 1493167514Skmacy if ((edata->addr & 0x3) != 0 || edata->addr >= sc->mmio_len) 1494167514Skmacy return (EFAULT); 1495167514Skmacy t3_write_reg(sc, edata->addr, edata->val); 1496167514Skmacy break; 1497167514Skmacy } 1498167514Skmacy case CHELSIO_GETREG: { 1499167514Skmacy struct ch_reg *edata = (struct ch_reg *)data; 1500167514Skmacy if ((edata->addr & 0x3) != 0 || edata->addr >= sc->mmio_len) 1501167514Skmacy return (EFAULT); 1502167514Skmacy edata->val = t3_read_reg(sc, edata->addr); 1503167514Skmacy break; 1504167514Skmacy } 1505167514Skmacy case CHELSIO_GET_SGE_CONTEXT: { 1506167514Skmacy struct ch_cntxt *ecntxt = (struct ch_cntxt *)data; 1507167514Skmacy mtx_lock(&sc->sge.reg_lock); 1508167514Skmacy switch (ecntxt->cntxt_type) { 1509167514Skmacy case CNTXT_TYPE_EGRESS: 1510167514Skmacy error = t3_sge_read_ecntxt(sc, ecntxt->cntxt_id, 1511167514Skmacy ecntxt->data); 1512167514Skmacy break; 1513167514Skmacy case CNTXT_TYPE_FL: 1514167514Skmacy error = t3_sge_read_fl(sc, ecntxt->cntxt_id, 1515167514Skmacy ecntxt->data); 1516167514Skmacy break; 1517167514Skmacy case CNTXT_TYPE_RSP: 1518167514Skmacy error = t3_sge_read_rspq(sc, ecntxt->cntxt_id, 1519167514Skmacy ecntxt->data); 1520167514Skmacy break; 1521167514Skmacy case CNTXT_TYPE_CQ: 1522167514Skmacy error = t3_sge_read_cq(sc, ecntxt->cntxt_id, 1523167514Skmacy ecntxt->data); 1524167514Skmacy break; 1525167514Skmacy default: 1526167514Skmacy error = EINVAL; 1527167514Skmacy break; 1528167514Skmacy } 1529167514Skmacy mtx_unlock(&sc->sge.reg_lock); 1530167514Skmacy break; 1531167514Skmacy } 1532167514Skmacy case CHELSIO_GET_SGE_DESC: { 1533167514Skmacy struct ch_desc *edesc = (struct ch_desc *)data; 1534167514Skmacy int ret; 1535167514Skmacy if (edesc->queue_num >= SGE_QSETS * 6) 1536167514Skmacy return (EINVAL); 1537167514Skmacy ret = t3_get_desc(&sc->sge.qs[edesc->queue_num / 6], 1538167514Skmacy edesc->queue_num % 6, edesc->idx, edesc->data); 1539167514Skmacy if (ret < 0) 1540167514Skmacy return (EINVAL); 1541167514Skmacy edesc->size = ret; 1542167514Skmacy break; 1543167514Skmacy } 1544167514Skmacy case CHELSIO_SET_QSET_PARAMS: { 1545167514Skmacy struct qset_params *q; 1546167514Skmacy struct ch_qset_params *t = (struct ch_qset_params *)data; 1547167514Skmacy 1548167514Skmacy if (t->qset_idx >= SGE_QSETS) 1549167514Skmacy return -EINVAL; 1550167514Skmacy if (!in_range(t->intr_lat, 0, M_NEWTIMER) || 1551167514Skmacy !in_range(t->cong_thres, 0, 255) || 1552167514Skmacy !in_range(t->txq_size[0], MIN_TXQ_ENTRIES, 1553167514Skmacy MAX_TXQ_ENTRIES) || 1554167514Skmacy !in_range(t->txq_size[1], MIN_TXQ_ENTRIES, 1555167514Skmacy MAX_TXQ_ENTRIES) || 1556167514Skmacy !in_range(t->txq_size[2], MIN_CTRL_TXQ_ENTRIES, 1557167514Skmacy MAX_CTRL_TXQ_ENTRIES) || 1558167514Skmacy !in_range(t->fl_size[0], MIN_FL_ENTRIES, MAX_RX_BUFFERS) || 1559167514Skmacy !in_range(t->fl_size[1], MIN_FL_ENTRIES, 1560167514Skmacy MAX_RX_JUMBO_BUFFERS) || 1561167514Skmacy !in_range(t->rspq_size, MIN_RSPQ_ENTRIES, MAX_RSPQ_ENTRIES)) 1562167514Skmacy return -EINVAL; 1563167514Skmacy if ((sc->flags & FULL_INIT_DONE) && 1564167514Skmacy (t->rspq_size >= 0 || t->fl_size[0] >= 0 || 1565167514Skmacy t->fl_size[1] >= 0 || t->txq_size[0] >= 0 || 1566167514Skmacy t->txq_size[1] >= 0 || t->txq_size[2] >= 0 || 1567167514Skmacy t->polling >= 0 || t->cong_thres >= 0)) 1568167514Skmacy return -EBUSY; 1569167514Skmacy 1570167514Skmacy q = &sc->params.sge.qset[t->qset_idx]; 1571167514Skmacy 1572167514Skmacy if (t->rspq_size >= 0) 1573167514Skmacy q->rspq_size = t->rspq_size; 1574167514Skmacy if (t->fl_size[0] >= 0) 1575167514Skmacy q->fl_size = t->fl_size[0]; 1576167514Skmacy if (t->fl_size[1] >= 0) 1577167514Skmacy q->jumbo_size = t->fl_size[1]; 1578167514Skmacy if (t->txq_size[0] >= 0) 1579167514Skmacy q->txq_size[0] = t->txq_size[0]; 1580167514Skmacy if (t->txq_size[1] >= 0) 1581167514Skmacy q->txq_size[1] = t->txq_size[1]; 1582167514Skmacy if (t->txq_size[2] >= 0) 1583167514Skmacy q->txq_size[2] = t->txq_size[2]; 1584167514Skmacy if (t->cong_thres >= 0) 1585167514Skmacy q->cong_thres = t->cong_thres; 1586167514Skmacy if (t->intr_lat >= 0) { 1587167514Skmacy struct sge_qset *qs = &sc->sge.qs[t->qset_idx]; 1588167514Skmacy 1589167514Skmacy q->coalesce_nsecs = t->intr_lat*1000; 1590167514Skmacy t3_update_qset_coalesce(qs, q); 1591167514Skmacy } 1592167514Skmacy break; 1593167514Skmacy } 1594167514Skmacy case CHELSIO_GET_QSET_PARAMS: { 1595167514Skmacy struct qset_params *q; 1596167514Skmacy struct ch_qset_params *t = (struct ch_qset_params *)data; 1597167514Skmacy 1598167514Skmacy if (t->qset_idx >= SGE_QSETS) 1599167514Skmacy return (EINVAL); 1600167514Skmacy 1601167514Skmacy q = &(sc)->params.sge.qset[t->qset_idx]; 1602167514Skmacy t->rspq_size = q->rspq_size; 1603167514Skmacy t->txq_size[0] = q->txq_size[0]; 1604167514Skmacy t->txq_size[1] = q->txq_size[1]; 1605167514Skmacy t->txq_size[2] = q->txq_size[2]; 1606167514Skmacy t->fl_size[0] = q->fl_size; 1607167514Skmacy t->fl_size[1] = q->jumbo_size; 1608167514Skmacy t->polling = q->polling; 1609167514Skmacy t->intr_lat = q->coalesce_nsecs / 1000; 1610167514Skmacy t->cong_thres = q->cong_thres; 1611167514Skmacy break; 1612167514Skmacy } 1613167514Skmacy case CHELSIO_SET_QSET_NUM: { 1614167514Skmacy struct ch_reg *edata = (struct ch_reg *)data; 1615167514Skmacy unsigned int port_idx = pi->port; 1616167514Skmacy 1617167514Skmacy if (sc->flags & FULL_INIT_DONE) 1618167514Skmacy return (EBUSY); 1619167514Skmacy if (edata->val < 1 || 1620167514Skmacy (edata->val > 1 && !(sc->flags & USING_MSIX))) 1621167514Skmacy return (EINVAL); 1622167514Skmacy if (edata->val + sc->port[!port_idx].nqsets > SGE_QSETS) 1623167514Skmacy return (EINVAL); 1624167514Skmacy sc->port[port_idx].nqsets = edata->val; 1625167514Skmacy /* 1626167514Skmacy * XXX we're hardcoding ourselves to 2 ports 1627167514Skmacy * just like the LEENUX 1628167514Skmacy */ 1629167514Skmacy sc->port[1].first_qset = sc->port[0].nqsets; 1630167514Skmacy break; 1631167514Skmacy } 1632167514Skmacy case CHELSIO_GET_QSET_NUM: { 1633167514Skmacy struct ch_reg *edata = (struct ch_reg *)data; 1634167514Skmacy edata->val = pi->nqsets; 1635167514Skmacy break; 1636167514Skmacy } 1637167514Skmacy#ifdef notyet 1638167514Skmacy /* 1639167514Skmacy * XXX FreeBSD driver does not currently support any 1640167514Skmacy * offload functionality 1641167514Skmacy */ 1642167514Skmacy case CHELSIO_LOAD_FW: 1643167514Skmacy case CHELSIO_DEVUP: 1644167514Skmacy case CHELSIO_SETMTUTAB: 1645167514Skmacy case CHELSIO_GET_PM: 1646167514Skmacy case CHELSIO_SET_PM: 1647167514Skmacy case CHELSIO_READ_TCAM_WORD: 1648167514Skmacy return (EOPNOTSUPP); 1649167514Skmacy break; 1650167514Skmacy#endif 1651167514Skmacy case CHELSIO_GET_MEM: { 1652167514Skmacy struct ch_mem_range *t = (struct ch_mem_range *)data; 1653167514Skmacy struct mc7 *mem; 1654167514Skmacy uint8_t *useraddr; 1655167514Skmacy u64 buf[32]; 1656167514Skmacy 1657167514Skmacy if (!is_offload(sc)) 1658167514Skmacy return (EOPNOTSUPP); 1659167514Skmacy if (!(sc->flags & FULL_INIT_DONE)) 1660167514Skmacy return (EIO); /* need the memory controllers */ 1661167514Skmacy if ((t->addr & 0x7) || (t->len & 0x7)) 1662167514Skmacy return (EINVAL); 1663167514Skmacy if (t->mem_id == MEM_CM) 1664167514Skmacy mem = &sc->cm; 1665167514Skmacy else if (t->mem_id == MEM_PMRX) 1666167514Skmacy mem = &sc->pmrx; 1667167514Skmacy else if (t->mem_id == MEM_PMTX) 1668167514Skmacy mem = &sc->pmtx; 1669167514Skmacy else 1670167514Skmacy return (EINVAL); 1671167514Skmacy 1672167514Skmacy /* 1673167514Skmacy * Version scheme: 1674167514Skmacy * bits 0..9: chip version 1675167514Skmacy * bits 10..15: chip revision 1676167514Skmacy */ 1677167514Skmacy t->version = 3 | (sc->params.rev << 10); 1678167514Skmacy 1679167514Skmacy /* 1680167514Skmacy * Read 256 bytes at a time as len can be large and we don't 1681167514Skmacy * want to use huge intermediate buffers. 1682167514Skmacy */ 1683167514Skmacy useraddr = (uint8_t *)(t + 1); /* advance to start of buffer */ 1684167514Skmacy while (t->len) { 1685167514Skmacy unsigned int chunk = min(t->len, sizeof(buf)); 1686167514Skmacy 1687167514Skmacy error = t3_mc7_bd_read(mem, t->addr / 8, chunk / 8, buf); 1688167514Skmacy if (error) 1689167514Skmacy return (-error); 1690167514Skmacy if (copyout(buf, useraddr, chunk)) 1691167514Skmacy return (EFAULT); 1692167514Skmacy useraddr += chunk; 1693167514Skmacy t->addr += chunk; 1694167514Skmacy t->len -= chunk; 1695167514Skmacy } 1696167514Skmacy break; 1697167514Skmacy } 1698167514Skmacy case CHELSIO_SET_TRACE_FILTER: { 1699167514Skmacy struct ch_trace *t = (struct ch_trace *)data; 1700167514Skmacy const struct trace_params *tp; 1701167514Skmacy 1702167514Skmacy tp = (const struct trace_params *)&t->sip; 1703167514Skmacy if (t->config_tx) 1704167514Skmacy t3_config_trace_filter(sc, tp, 0, t->invert_match, 1705167514Skmacy t->trace_tx); 1706167514Skmacy if (t->config_rx) 1707167514Skmacy t3_config_trace_filter(sc, tp, 1, t->invert_match, 1708167514Skmacy t->trace_rx); 1709167514Skmacy break; 1710167514Skmacy } 1711167514Skmacy case CHELSIO_SET_PKTSCHED: { 1712167514Skmacy struct ch_pktsched_params *p = (struct ch_pktsched_params *)data; 1713167514Skmacy if (sc->open_device_map == 0) 1714167514Skmacy return (EAGAIN); 1715167514Skmacy send_pktsched_cmd(sc, p->sched, p->idx, p->min, p->max, 1716167514Skmacy p->binding); 1717167514Skmacy break; 1718167514Skmacy } 1719167514Skmacy case CHELSIO_IFCONF_GETREGS: { 1720167514Skmacy struct ifconf_regs *regs = (struct ifconf_regs *)data; 1721167514Skmacy int reglen = cxgb_get_regs_len(); 1722167514Skmacy uint8_t *buf = malloc(REGDUMP_SIZE, M_DEVBUF, M_NOWAIT); 1723167514Skmacy if (buf == NULL) { 1724167514Skmacy return (ENOMEM); 1725167514Skmacy } if (regs->len > reglen) 1726167514Skmacy regs->len = reglen; 1727167514Skmacy else if (regs->len < reglen) { 1728167514Skmacy error = E2BIG; 1729167514Skmacy goto done; 1730167514Skmacy } 1731167514Skmacy cxgb_get_regs(sc, regs, buf); 1732167514Skmacy error = copyout(buf, regs->data, reglen); 1733167514Skmacy 1734167514Skmacy done: 1735167514Skmacy free(buf, M_DEVBUF); 1736167514Skmacy 1737167514Skmacy break; 1738167514Skmacy } 1739167514Skmacy default: 1740167514Skmacy return (EOPNOTSUPP); 1741167514Skmacy break; 1742167514Skmacy } 1743167514Skmacy 1744167514Skmacy return (error); 1745167514Skmacy} 1746167514Skmacy 1747167514Skmacystatic __inline void 1748167514Skmacyreg_block_dump(struct adapter *ap, uint8_t *buf, unsigned int start, 1749167514Skmacy unsigned int end) 1750167514Skmacy{ 1751167514Skmacy uint32_t *p = (uint32_t *)buf + start; 1752167514Skmacy 1753167514Skmacy for ( ; start <= end; start += sizeof(uint32_t)) 1754167514Skmacy *p++ = t3_read_reg(ap, start); 1755167514Skmacy} 1756167514Skmacy 1757167514Skmacy#define T3_REGMAP_SIZE (3 * 1024) 1758167514Skmacystatic int 1759167514Skmacycxgb_get_regs_len(void) 1760167514Skmacy{ 1761167514Skmacy return T3_REGMAP_SIZE; 1762167514Skmacy} 1763167514Skmacy#undef T3_REGMAP_SIZE 1764167514Skmacy 1765167514Skmacystatic void 1766167514Skmacycxgb_get_regs(adapter_t *sc, struct ifconf_regs *regs, uint8_t *buf) 1767167514Skmacy{ 1768167514Skmacy 1769167514Skmacy /* 1770167514Skmacy * Version scheme: 1771167514Skmacy * bits 0..9: chip version 1772167514Skmacy * bits 10..15: chip revision 1773167514Skmacy * bit 31: set for PCIe cards 1774167514Skmacy */ 1775167514Skmacy regs->version = 3 | (sc->params.rev << 10) | (is_pcie(sc) << 31); 1776167514Skmacy 1777167514Skmacy /* 1778167514Skmacy * We skip the MAC statistics registers because they are clear-on-read. 1779167514Skmacy * Also reading multi-register stats would need to synchronize with the 1780167514Skmacy * periodic mac stats accumulation. Hard to justify the complexity. 1781167514Skmacy */ 1782167514Skmacy memset(buf, 0, REGDUMP_SIZE); 1783167514Skmacy reg_block_dump(sc, buf, 0, A_SG_RSPQ_CREDIT_RETURN); 1784167514Skmacy reg_block_dump(sc, buf, A_SG_HI_DRB_HI_THRSH, A_ULPRX_PBL_ULIMIT); 1785167514Skmacy reg_block_dump(sc, buf, A_ULPTX_CONFIG, A_MPS_INT_CAUSE); 1786167514Skmacy reg_block_dump(sc, buf, A_CPL_SWITCH_CNTRL, A_CPL_MAP_TBL_DATA); 1787167514Skmacy reg_block_dump(sc, buf, A_SMB_GLOBAL_TIME_CFG, A_XGM_SERDES_STAT3); 1788167514Skmacy reg_block_dump(sc, buf, A_XGM_SERDES_STATUS0, 1789167514Skmacy XGM_REG(A_XGM_SERDES_STAT3, 1)); 1790167514Skmacy reg_block_dump(sc, buf, XGM_REG(A_XGM_SERDES_STATUS0, 1), 1791167514Skmacy XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1)); 1792167514Skmacy} 1793