/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2018 Joyent, Inc. */ /* * This file is part of the Chelsio T1 Ethernet driver. * * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved. */ /* * Solaris Multithreaded STREAMS DLPI Chelsio PCI Ethernet Driver */ /* #define CH_DEBUG 1 */ #ifdef CH_DEBUG #define DEBUG_ENTER(a) debug_enter(a) #define PRINT(a) printf a #else #define DEBUG_ENTER(a) #define PRINT(a) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ostypes.h" #include "common.h" #include "oschtoe.h" #include "sge.h" #include "regs.h" #include "ch.h" /* Chelsio Driver specific parameters */ #include "version.h" /* * Function prototypes. */ static int ch_attach(dev_info_t *, ddi_attach_cmd_t); static int ch_detach(dev_info_t *, ddi_detach_cmd_t); static int ch_quiesce(dev_info_t *); static void ch_free_dma_handles(ch_t *chp); static void ch_set_name(ch_t *chp, int unit); static void ch_free_name(ch_t *chp); static void ch_get_prop(ch_t *chp); #if defined(__sparc) static void ch_free_dvma_handles(ch_t *chp); #endif /* GLD interfaces */ static int ch_reset(gld_mac_info_t *); static int ch_start(gld_mac_info_t *); static int ch_stop(gld_mac_info_t *); static int ch_set_mac_address(gld_mac_info_t *, uint8_t *); static int ch_set_multicast(gld_mac_info_t *, uint8_t *, int); static int ch_ioctl(gld_mac_info_t *, queue_t *, mblk_t *); static int ch_set_promiscuous(gld_mac_info_t *, int); static int ch_get_stats(gld_mac_info_t *, struct gld_stats *); static int ch_send(gld_mac_info_t *, mblk_t *); static uint_t ch_intr(gld_mac_info_t *); /* * Data access requirements. */ static struct ddi_device_acc_attr le_attr = { DDI_DEVICE_ATTR_V0, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC }; /* * No swap mapping device attributes */ static struct ddi_device_acc_attr null_attr = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC }; /* * STREAMS driver identification struture module_info(9s) * * driver limit values */ static struct module_info ch_minfo = { CHIDNUM, /* mi_idnum */ CHNAME, /* mi_idname */ CHMINPSZ, /* mi_minpsz */ CHMAXPSZ, /* mi_maxpsz */ CHHIWAT, /* mi_hiwat */ CHLOWAT /* mi_lowat */ }; /* * STREAMS queue processiong procedures qinit(9s) * * read queue procedures */ static struct qinit ch_rinit = { (int (*)()) NULL, /* qi_putp */ gld_rsrv, /* qi_srvp */ gld_open, /* qi_qopen */ gld_close, /* qi_qclose */ (int (*)()) NULL, /* qi_qadmin */ &ch_minfo, /* qi_minfo */ NULL /* qi_mstat */ }; /* * STREAMS queue processiong procedures qinit(9s) * * write queue procedures */ static struct qinit ch_winit = { gld_wput, /* qi_putp */ gld_wsrv, /* qi_srvp */ (int (*)()) NULL, /* qi_qopen */ (int (*)()) NULL, /* qi_qclose */ (int (*)()) NULL, /* qi_qadmin */ &ch_minfo, /* qi_minfo */ NULL /* qi_mstat */ }; /* * STREAMS entity declaration structure - streamtab(9s) */ static struct streamtab chinfo = { &ch_rinit, /* read queue information */ &ch_winit, /* write queue information */ NULL, /* st_muxrinit */ NULL /* st_muxwrinit */ }; /* * Device driver ops vector - cb_ops(9s) * * charater/block entry points structure. * chinfo identifies driver as a STREAMS driver. */ static struct cb_ops cb_ch_ops = { nulldev, /* cb_open */ nulldev, /* cb_close */ nodev, /* cb_strategy */ nodev, /* cb_print */ nodev, /* cb_dump */ nodev, /* cb_read */ nodev, /* cb_write */ nodev, /* cb_ioctl */ nodev, /* cb_devmap */ nodev, /* cb_mmap */ nodev, /* cb_segmap */ nochpoll, /* cb_chpoll */ ddi_prop_op, /* report driver property information - prop_op(9e) */ &chinfo, /* cb_stream */ #if defined(__sparc) D_MP | D_64BIT, #else D_MP, /* cb_flag (supports multi-threading) */ #endif CB_REV, /* cb_rev */ nodev, /* cb_aread */ nodev /* cb_awrite */ }; /* * dev_ops(9S) structure * * Device Operations table, for autoconfiguration */ static struct dev_ops ch_ops = { DEVO_REV, /* Driver build version */ 0, /* Initial driver reference count */ gld_getinfo, /* funcp: get driver information - getinfo(9e) */ nulldev, /* funcp: entry point obsolute - identify(9e) */ nulldev, /* funp: probe for device - probe(9e) */ ch_attach, /* funp: attach driver to dev_info - attach(9e) */ ch_detach, /* funp: detach driver to unload - detach(9e) */ nodev, /* funp: reset device (not supported) - dev_ops(9s) */ &cb_ch_ops, /* ptr to cb_ops structure */ NULL, /* ptr to nexus bus operations structure (leaf) */ NULL, /* funp: change device power level - power(9e) */ ch_quiesce, /* devo_quiesce */ }; /* * modldrv(9s) structure * * Definition for module specific device driver linkage structures (modctl.h) */ static struct modldrv modldrv = { &mod_driverops, /* driver module */ VERSION, &ch_ops, /* driver ops */ }; /* * modlinkage(9s) structure * * module linkage base structure (modctl.h) */ static struct modlinkage modlinkage = { MODREV_1, /* revision # of system */ &modldrv, /* NULL terminated list of linkage strucures */ NULL }; /* ===================== start of STREAMS driver code ================== */ #ifdef CONFIG_CHELSIO_T1_OFFLOAD /* * global pointer to toe per-driver control structure. */ #define MAX_CARDS 4 ch_t *gchp[MAX_CARDS]; #endif kmutex_t in_use_l; uint32_t buffers_in_use[SZ_INUSE]; uint32_t in_use_index; /* * Ethernet broadcast address definition. */ static struct ether_addr etherbroadcastaddr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* * Module initialization functions. * * Routine Called by * _init(9E) modload(9F) * _info(9E) modinfo(9F) * _fini(9E) modunload(9F) */ /* * _init(9E): * * Initial, one-time, resource allocation and data initialization. */ int _init(void) { int status; status = mod_install(&modlinkage); mutex_init(&in_use_l, NULL, MUTEX_DRIVER, NULL); return (status); } /* * _fini(9E): It is here that any device information that was allocated * during the _init(9E) routine should be released and the module removed * from the system. In the case of per-instance information, that information * should be released in the _detach(9E) routine. */ int _fini(void) { int status; int i; uint32_t t = 0; for (i = 0; i < SZ_INUSE; i++) t += buffers_in_use[i]; if (t != 0) return (DDI_FAILURE); status = mod_remove(&modlinkage); if (status == DDI_SUCCESS) mutex_destroy(&in_use_l); return (status); } int _info(struct modinfo *modinfop) { int status; status = mod_info(&modlinkage, modinfop); return (status); } /* * Attach(9E) - This is called on the open to the device. It creates * an instance of the driver. In this routine we create the minor * device node. The routine also initializes all per-unit * mutex's and conditional variables. * * If we were resuming a suspended instance of a device due to power * management, then that would be handled here as well. For more on * that subject see the man page for pm(9E) * * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. */ int chdebug = 0; int ch_abort_debug = 0; static int ch_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { ch_t *chp; int rv; int unit; #ifdef CH_DEBUG int Version; int VendorID; int DeviceID; int SubDeviceID; int Command; #endif gld_mac_info_t *macinfo; /* GLD stuff follows */ char *driver; if (ch_abort_debug) debug_enter("ch_attach"); if (chdebug) return (DDI_FAILURE); if (cmd == DDI_ATTACH) { unit = ddi_get_instance(dip); driver = (char *)ddi_driver_name(dip); PRINT(("driver %s unit: %d\n", driver, unit)); macinfo = gld_mac_alloc(dip); if (macinfo == NULL) { PRINT(("macinfo allocation failed\n")); DEBUG_ENTER("ch_attach"); return (DDI_FAILURE); } chp = (ch_t *)kmem_zalloc(sizeof (ch_t), KM_SLEEP); if (chp == NULL) { PRINT(("zalloc of chp failed\n")); DEBUG_ENTER("ch_attach"); gld_mac_free(macinfo); return (DDI_FAILURE); } #ifdef CONFIG_CHELSIO_T1_OFFLOAD /* Solaris TOE support */ gchp[unit] = chp; #endif PRINT(("attach macinfo: %p chp: %p\n", macinfo, chp)); chp->ch_dip = dip; chp->ch_macp = macinfo; chp->ch_unit = unit; ch_set_name(chp, unit); /* * map in PCI register spaces * * PCI register set 0 - PCI configuration space * PCI register set 1 - T101 card register space #1 */ /* map in T101 PCI configuration space */ rv = pci_config_setup( dip, /* ptr to dev's dev_info struct */ &chp->ch_hpci); /* ptr to data access handle */ if (rv != DDI_SUCCESS) { PRINT(("PCI config setup failed\n")); DEBUG_ENTER("ch_attach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[unit] = NULL; #endif cmn_err(CE_WARN, "%s: ddi_config_setup PCI error %d\n", chp->ch_name, rv); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } ch_get_prop(chp); macinfo->gldm_devinfo = dip; macinfo->gldm_private = (caddr_t)chp; macinfo->gldm_reset = ch_reset; macinfo->gldm_start = ch_start; macinfo->gldm_stop = ch_stop; macinfo->gldm_set_mac_addr = ch_set_mac_address; macinfo->gldm_send = ch_send; macinfo->gldm_set_promiscuous = ch_set_promiscuous; macinfo->gldm_get_stats = ch_get_stats; macinfo->gldm_ioctl = ch_ioctl; macinfo->gldm_set_multicast = ch_set_multicast; macinfo->gldm_intr = ch_intr; macinfo->gldm_mctl = NULL; macinfo->gldm_ident = driver; macinfo->gldm_type = DL_ETHER; macinfo->gldm_minpkt = 0; macinfo->gldm_maxpkt = chp->ch_mtu; macinfo->gldm_addrlen = ETHERADDRL; macinfo->gldm_saplen = -2; macinfo->gldm_ppa = unit; macinfo->gldm_broadcast_addr = etherbroadcastaddr.ether_addr_octet; /* * do a power reset of card * * 1. set PwrState to D3hot (3) * 2. clear PwrState flags */ pci_config_put32(chp->ch_hpci, 0x44, 3); pci_config_put32(chp->ch_hpci, 0x44, 0); /* delay .5 sec */ DELAY(500000); #ifdef CH_DEBUG VendorID = pci_config_get16(chp->ch_hpci, 0); DeviceID = pci_config_get16(chp->ch_hpci, 2); SubDeviceID = pci_config_get16(chp->ch_hpci, 0x2e); Command = pci_config_get16(chp->ch_hpci, 4); PRINT(("IDs: %x,%x,%x\n", VendorID, DeviceID, SubDeviceID)); PRINT(("Command: %x\n", Command)); #endif /* map in T101 register space (BAR0) */ rv = ddi_regs_map_setup( dip, /* ptr to dev's dev_info struct */ BAR0, /* register address space */ &chp->ch_bar0, /* address of offset */ 0, /* offset into register address space */ 0, /* length mapped (everything) */ &le_attr, /* ptr to device attr structure */ &chp->ch_hbar0); /* ptr to data access handle */ if (rv != DDI_SUCCESS) { PRINT(("map registers failed\n")); DEBUG_ENTER("ch_attach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[unit] = NULL; #endif cmn_err(CE_WARN, "%s: ddi_regs_map_setup BAR0 error %d\n", chp->ch_name, rv); pci_config_teardown(&chp->ch_hpci); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } #ifdef CH_DEBUG Version = ddi_get32(chp->ch_hbar0, (uint32_t *)(chp->ch_bar0+0x6c)); #endif (void) ddi_dev_regsize(dip, 1, &chp->ch_bar0sz); PRINT(("PCI BAR0 space addr: %p\n", chp->ch_bar0)); PRINT(("PCI BAR0 space size: %x\n", chp->ch_bar0sz)); PRINT(("PE Version: %x\n", Version)); /* * Add interrupt to system. */ rv = ddi_get_iblock_cookie( dip, /* ptr to dev's dev_info struct */ 0, /* interrupt # (0) */ &chp->ch_icookp); /* ptr to interrupt block cookie */ if (rv != DDI_SUCCESS) { PRINT(("iblock cookie failed\n")); DEBUG_ENTER("ch_attach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[unit] = NULL; #endif cmn_err(CE_WARN, "%s: ddi_get_iblock_cookie error %d\n", chp->ch_name, rv); ddi_regs_map_free(&chp->ch_hbar0); pci_config_teardown(&chp->ch_hpci); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } /* * add interrupt handler before card setup. */ rv = ddi_add_intr( dip, /* ptr to dev's dev_info struct */ 0, /* interrupt # (0) */ 0, /* iblock cookie ptr (NULL) */ 0, /* idevice cookie ptr (NULL) */ gld_intr, /* function ptr to interrupt handler */ (caddr_t)macinfo); /* handler argument */ if (rv != DDI_SUCCESS) { PRINT(("add_intr failed\n")); DEBUG_ENTER("ch_attach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[unit] = NULL; #endif cmn_err(CE_WARN, "%s: ddi_add_intr error %d\n", chp->ch_name, rv); ddi_regs_map_free(&chp->ch_hbar0); pci_config_teardown(&chp->ch_hpci); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } /* initalize all the remaining per-card locks */ mutex_init(&chp->ch_lock, NULL, MUTEX_DRIVER, (void *)chp->ch_icookp); mutex_init(&chp->ch_intr, NULL, MUTEX_DRIVER, (void *)chp->ch_icookp); mutex_init(&chp->ch_mc_lck, NULL, MUTEX_DRIVER, NULL); mutex_init(&chp->ch_dh_lck, NULL, MUTEX_DRIVER, NULL); mutex_init(&chp->mac_lock, NULL, MUTEX_DRIVER, NULL); /* ------- initialize Chelsio card ------- */ if (pe_attach(chp)) { PRINT(("card initialization failed\n")); DEBUG_ENTER("ch_attach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[unit] = NULL; #endif cmn_err(CE_WARN, "%s: pe_attach failed\n", chp->ch_name); mutex_destroy(&chp->ch_lock); mutex_destroy(&chp->ch_intr); mutex_destroy(&chp->ch_mc_lck); mutex_destroy(&chp->ch_dh_lck); mutex_destroy(&chp->mac_lock); ddi_remove_intr(dip, 0, chp->ch_icookp); ddi_regs_map_free(&chp->ch_hbar0); pci_config_teardown(&chp->ch_hpci); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } /* ------- done with Chelsio card ------- */ /* now can set mac address */ macinfo->gldm_vendor_addr = pe_get_mac(chp); macinfo->gldm_cookie = chp->ch_icookp; /* * We only active checksum offload for T2 architectures. */ if (is_T2(chp)) { if (chp->ch_config.cksum_enabled) macinfo->gldm_capabilities |= GLD_CAP_CKSUM_FULL_V4; } else chp->ch_config.cksum_enabled = 0; rv = gld_register( dip, /* ptr to dev's dev_info struct */ (char *)ddi_driver_name(dip), /* driver name */ macinfo); /* ptr to gld macinfo buffer */ /* * The Jumbo frames capability is not yet available * in Solaris 10 so registration will fail. MTU > 1500 is * supported in Update 1. */ if (rv != DDI_SUCCESS) { cmn_err(CE_NOTE, "MTU > 1500 not supported by GLD.\n"); cmn_err(CE_NOTE, "Setting MTU to 1500. \n"); macinfo->gldm_maxpkt = chp->ch_mtu = 1500; rv = gld_register( dip, /* ptr to dev's dev_info struct */ (char *)ddi_driver_name(dip), /* driver name */ macinfo); /* ptr to gld macinfo buffer */ } if (rv != DDI_SUCCESS) { PRINT(("gld_register failed\n")); DEBUG_ENTER("ch_attach"); cmn_err(CE_WARN, "%s: gld_register error %d\n", chp->ch_name, rv); pe_detach(chp); mutex_destroy(&chp->ch_lock); mutex_destroy(&chp->ch_intr); mutex_destroy(&chp->ch_mc_lck); mutex_destroy(&chp->ch_dh_lck); mutex_destroy(&chp->mac_lock); ddi_remove_intr(dip, 0, chp->ch_icookp); ddi_regs_map_free(&chp->ch_hbar0); pci_config_teardown(&chp->ch_hpci); ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); return (DDI_FAILURE); } /* * print a banner at boot time (verbose mode), announcing * the device pointed to by dip */ ddi_report_dev(dip); if (ch_abort_debug) debug_enter("ch_attach"); return (DDI_SUCCESS); } else if (cmd == DDI_RESUME) { PRINT(("attach resume\n")); DEBUG_ENTER("ch_attach"); if ((chp = (ch_t *)ddi_get_driver_private(dip)) == NULL) return (DDI_FAILURE); mutex_enter(&chp->ch_lock); chp->ch_flags &= ~PESUSPENDED; mutex_exit(&chp->ch_lock); return (DDI_SUCCESS); } else { PRINT(("attach: bad command\n")); DEBUG_ENTER("ch_attach"); return (DDI_FAILURE); } } /* * quiesce(9E) entry point. * * This function is called when the system is single-threaded at high * PIL with preemption disabled. Therefore, this function must not be * blocked. * * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. * DDI_FAILURE indicates an error condition and should almost never happen. */ static int ch_quiesce(dev_info_t *dip) { ch_t *chp; gld_mac_info_t *macinfo = (gld_mac_info_t *)ddi_get_driver_private(dip); chp = (ch_t *)macinfo->gldm_private; chdebug = 0; ch_abort_debug = 0; #ifdef CONFIG_CHELSIO_T1_OFFLOAD gchp[chp->ch_unit] = NULL; #endif /* Set driver state for this card to IDLE */ chp->ch_state = PEIDLE; /* * Do a power reset of card * 1. set PwrState to D3hot (3) * 2. clear PwrState flags */ pci_config_put32(chp->ch_hpci, 0x44, 3); pci_config_put32(chp->ch_hpci, 0x44, 0); /* Wait 0.5 sec */ drv_usecwait(500000); /* * Now stop the chip */ chp->ch_refcnt = 0; chp->ch_state = PESTOP; /* Disables all interrupts */ t1_interrupts_disable(chp); /* Disables SGE queues */ t1_write_reg_4(chp->sge->obj, A_SG_CONTROL, 0x0); t1_write_reg_4(chp->sge->obj, A_SG_INT_CAUSE, 0x0); return (DDI_SUCCESS); } static int ch_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { gld_mac_info_t *macinfo; ch_t *chp; if (cmd == DDI_DETACH) { macinfo = (gld_mac_info_t *)ddi_get_driver_private(dip); chp = (ch_t *)macinfo->gldm_private; /* * fail detach if there are outstanding mblks still * in use somewhere. */ DEBUG_ENTER("ch_detach"); #ifdef CONFIG_CHELSIO_T1_OFFLOAD mutex_enter(&chp->ch_lock); if (chp->ch_refcnt > 0) { mutex_exit(&chp->ch_lock); return (DDI_FAILURE); } mutex_exit(&chp->ch_lock); gchp[chp->ch_unit] = NULL; #endif /* * set driver state for this card to IDLE. We're * shutting down. */ mutex_enter(&chp->ch_lock); chp->ch_state = PEIDLE; mutex_exit(&chp->ch_lock); /* * do a power reset of card * * 1. set PwrState to D3hot (3) * 2. clear PwrState flags */ pci_config_put32(chp->ch_hpci, 0x44, 3); pci_config_put32(chp->ch_hpci, 0x44, 0); /* delay .5 sec */ DELAY(500000); /* free register resources */ (void) gld_unregister(macinfo); /* make sure no interrupts while shutting down card */ ddi_remove_intr(dip, 0, chp->ch_icookp); /* * reset device and recover resources */ pe_detach(chp); ddi_regs_map_free(&chp->ch_hbar0); pci_config_teardown(&chp->ch_hpci); mutex_destroy(&chp->ch_lock); mutex_destroy(&chp->ch_intr); mutex_destroy(&chp->ch_mc_lck); mutex_destroy(&chp->ch_dh_lck); mutex_destroy(&chp->mac_lock); ch_free_dma_handles(chp); #if defined(__sparc) ch_free_dvma_handles(chp); #endif ch_free_name(chp); kmem_free(chp, sizeof (ch_t)); gld_mac_free(macinfo); DEBUG_ENTER("ch_detach end"); return (DDI_SUCCESS); } else if ((cmd == DDI_SUSPEND) || (cmd == DDI_PM_SUSPEND)) { DEBUG_ENTER("suspend"); if ((chp = (ch_t *)ddi_get_driver_private(dip)) == NULL) return (DDI_FAILURE); mutex_enter(&chp->ch_lock); chp->ch_flags |= PESUSPENDED; mutex_exit(&chp->ch_lock); #ifdef TODO /* Un-initialize (STOP) T101 */ #endif return (DDI_SUCCESS); } else return (DDI_FAILURE); } /* * ch_alloc_dma_mem * * allocates DMA handle * allocates kernel memory * allocates DMA access handle * * chp - per-board descriptor * type - byteswap mapping? * flags - type of mapping * size - # bytes mapped * paddr - physical address * dh - ddi dma handle * ah - ddi access handle */ void * ch_alloc_dma_mem(ch_t *chp, int type, int flags, int size, uint64_t *paddr, ulong_t *dh, ulong_t *ah) { ddi_dma_attr_t ch_dma_attr; ddi_dma_cookie_t cookie; ddi_dma_handle_t ch_dh; ddi_acc_handle_t ch_ah; ddi_device_acc_attr_t *dev_attrp; caddr_t ch_vaddr; size_t rlen; uint_t count; uint_t mapping; uint_t align; uint_t rv; uint_t direction; mapping = (flags&DMA_STREAM)?DDI_DMA_STREAMING:DDI_DMA_CONSISTENT; if (flags & DMA_4KALN) align = 0x4000; else if (flags & DMA_SMALN) align = chp->ch_sm_buf_aln; else if (flags & DMA_BGALN) align = chp->ch_bg_buf_aln; else { cmn_err(CE_WARN, "ch_alloc_dma_mem(%s): bad alignment flag\n", chp->ch_name); return (0); } direction = (flags&DMA_OUT)?DDI_DMA_WRITE:DDI_DMA_READ; /* * dynamically create a dma attribute structure */ ch_dma_attr.dma_attr_version = DMA_ATTR_V0; ch_dma_attr.dma_attr_addr_lo = 0; ch_dma_attr.dma_attr_addr_hi = 0xffffffffffffffff; ch_dma_attr.dma_attr_count_max = 0x00ffffff; ch_dma_attr.dma_attr_align = align; ch_dma_attr.dma_attr_burstsizes = 0xfff; ch_dma_attr.dma_attr_minxfer = 1; ch_dma_attr.dma_attr_maxxfer = 0x00ffffff; ch_dma_attr.dma_attr_seg = 0xffffffff; ch_dma_attr.dma_attr_sgllen = 1; ch_dma_attr.dma_attr_granular = 1; ch_dma_attr.dma_attr_flags = 0; rv = ddi_dma_alloc_handle( chp->ch_dip, /* device dev_info structure */ &ch_dma_attr, /* DMA attributes */ DDI_DMA_SLEEP, /* Wait if no memory */ NULL, /* no argument to callback */ &ch_dh); /* DMA handle */ if (rv != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ch_alloc_dma_mem: ddi_dma_alloc_handle error %d\n", chp->ch_name, rv); return (0); } /* set byte order for data xfer */ if (type) dev_attrp = &null_attr; else dev_attrp = &le_attr; rv = ddi_dma_mem_alloc( ch_dh, /* dma handle */ size, /* size desired allocate */ dev_attrp, /* access attributes */ mapping, DDI_DMA_SLEEP, /* wait for resources */ NULL, /* no argument */ &ch_vaddr, /* allocated memory */ &rlen, /* real size allocated */ &ch_ah); /* data access handle */ if (rv != DDI_SUCCESS) { ddi_dma_free_handle(&ch_dh); cmn_err(CE_WARN, "%s: ch_alloc_dma_mem: ddi_dma_mem_alloc error %d\n", chp->ch_name, rv); return (0); } rv = ddi_dma_addr_bind_handle( ch_dh, /* dma handle */ (struct as *)0, /* kernel address space */ ch_vaddr, /* virtual address */ rlen, /* length of object */ direction|mapping, DDI_DMA_SLEEP, /* Wait for resources */ NULL, /* no argument */ &cookie, /* dma cookie */ &count); if (rv != DDI_DMA_MAPPED) { ddi_dma_mem_free(&ch_ah); ddi_dma_free_handle(&ch_dh); cmn_err(CE_WARN, "%s: ch_alloc_dma_mem: ddi_dma_addr_bind_handle error %d\n", chp->ch_name, rv); return (0); } if (count != 1) { cmn_err(CE_WARN, "%s: ch_alloc_dma_mem: ch_alloc_dma_mem cookie count %d\n", chp->ch_name, count); PRINT(("ch_alloc_dma_mem cookie count %d\n", count)); ddi_dma_mem_free(&ch_ah); ddi_dma_free_handle(&ch_dh); return (0); } *paddr = cookie.dmac_laddress; *(ddi_dma_handle_t *)dh = ch_dh; *(ddi_acc_handle_t *)ah = ch_ah; return ((void *)ch_vaddr); } /* * ch_free_dma_mem * * frees resources allocated by ch_alloc_dma_mem() * * frees DMA handle * frees kernel memory * frees DMA access handle */ void ch_free_dma_mem(ulong_t dh, ulong_t ah) { ddi_dma_handle_t ch_dh = (ddi_dma_handle_t)dh; ddi_acc_handle_t ch_ah = (ddi_acc_handle_t)ah; (void) ddi_dma_unbind_handle(ch_dh); ddi_dma_mem_free(&ch_ah); ddi_dma_free_handle(&ch_dh); } /* * create a dma handle and return a dma handle entry. */ free_dh_t * ch_get_dma_handle(ch_t *chp) { ddi_dma_handle_t ch_dh; ddi_dma_attr_t ch_dma_attr; free_dh_t *dhe; int rv; dhe = (free_dh_t *)kmem_zalloc(sizeof (*dhe), KM_SLEEP); ch_dma_attr.dma_attr_version = DMA_ATTR_V0; ch_dma_attr.dma_attr_addr_lo = 0; ch_dma_attr.dma_attr_addr_hi = 0xffffffffffffffff; ch_dma_attr.dma_attr_count_max = 0x00ffffff; ch_dma_attr.dma_attr_align = 1; ch_dma_attr.dma_attr_burstsizes = 0xfff; ch_dma_attr.dma_attr_minxfer = 1; ch_dma_attr.dma_attr_maxxfer = 0x00ffffff; ch_dma_attr.dma_attr_seg = 0xffffffff; ch_dma_attr.dma_attr_sgllen = 5; ch_dma_attr.dma_attr_granular = 1; ch_dma_attr.dma_attr_flags = 0; rv = ddi_dma_alloc_handle( chp->ch_dip, /* device dev_info */ &ch_dma_attr, /* DMA attributes */ DDI_DMA_SLEEP, /* Wait if no memory */ NULL, /* no argument */ &ch_dh); /* DMA handle */ if (rv != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ch_get_dma_handle: ddi_dma_alloc_handle error %d\n", chp->ch_name, rv); kmem_free(dhe, sizeof (*dhe)); return ((free_dh_t *)0); } dhe->dhe_dh = (ulong_t)ch_dh; return (dhe); } /* * free the linked list of dma descriptor entries. */ static void ch_free_dma_handles(ch_t *chp) { free_dh_t *dhe, *the; dhe = chp->ch_dh; while (dhe) { ddi_dma_free_handle((ddi_dma_handle_t *)&dhe->dhe_dh); the = dhe; dhe = dhe->dhe_next; kmem_free(the, sizeof (*the)); } chp->ch_dh = NULL; } /* * ch_bind_dma_handle() * * returns # of entries used off of cmdQ_ce_t array to hold physical addrs. * * chp - per-board descriptor * size - # bytes mapped * vaddr - virtual address * cmp - array of cmdQ_ce_t entries * cnt - # free entries in cmp array */ uint32_t ch_bind_dma_handle(ch_t *chp, int size, caddr_t vaddr, cmdQ_ce_t *cmp, uint32_t cnt) { ddi_dma_cookie_t cookie; ddi_dma_handle_t ch_dh; uint_t count; uint32_t n = 1; free_dh_t *dhe; uint_t rv; mutex_enter(&chp->ch_dh_lck); if ((dhe = chp->ch_dh) != NULL) { chp->ch_dh = dhe->dhe_next; } mutex_exit(&chp->ch_dh_lck); if (dhe == NULL) { return (0); } ch_dh = (ddi_dma_handle_t)dhe->dhe_dh; rv = ddi_dma_addr_bind_handle( ch_dh, /* dma handle */ (struct as *)0, /* kernel address space */ vaddr, /* virtual address */ size, /* length of object */ DDI_DMA_WRITE|DDI_DMA_STREAMING, DDI_DMA_SLEEP, /* Wait for resources */ NULL, /* no argument */ &cookie, /* dma cookie */ &count); if (rv != DDI_DMA_MAPPED) { /* return dma header descriptor back to free list */ mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_dh; chp->ch_dh = dhe; mutex_exit(&chp->ch_dh_lck); cmn_err(CE_WARN, "%s: ch_bind_dma_handle: ddi_dma_addr_bind_handle err %d\n", chp->ch_name, rv); return (0); } /* * abort if we've run out of space */ if (count > cnt) { /* return dma header descriptor back to free list */ mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_dh; chp->ch_dh = dhe; mutex_exit(&chp->ch_dh_lck); return (0); } cmp->ce_pa = cookie.dmac_laddress; cmp->ce_dh = NULL; cmp->ce_len = cookie.dmac_size; cmp->ce_mp = NULL; cmp->ce_flg = DH_DMA; while (--count) { cmp++; n++; ddi_dma_nextcookie(ch_dh, &cookie); cmp->ce_pa = cookie.dmac_laddress; cmp->ce_dh = NULL; cmp->ce_len = cookie.dmac_size; cmp->ce_mp = NULL; cmp->ce_flg = DH_DMA; } cmp->ce_dh = dhe; return (n); } /* * ch_unbind_dma_handle() * * frees resources alloacted by ch_bind_dma_handle(). * * frees DMA handle */ void ch_unbind_dma_handle(ch_t *chp, free_dh_t *dhe) { ddi_dma_handle_t ch_dh = (ddi_dma_handle_t)dhe->dhe_dh; if (ddi_dma_unbind_handle(ch_dh)) cmn_err(CE_WARN, "%s: ddi_dma_unbind_handle failed", chp->ch_name); mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_dh; chp->ch_dh = dhe; mutex_exit(&chp->ch_dh_lck); } #if defined(__sparc) /* * DVMA stuff. Solaris only. */ /* * create a dvma handle and return a dma handle entry. * DVMA is on sparc only! */ free_dh_t * ch_get_dvma_handle(ch_t *chp) { ddi_dma_handle_t ch_dh; ddi_dma_lim_t ch_dvma_attr; free_dh_t *dhe; int rv; dhe = (free_dh_t *)kmem_zalloc(sizeof (*dhe), KM_SLEEP); ch_dvma_attr.dlim_addr_lo = 0; ch_dvma_attr.dlim_addr_hi = 0xffffffff; ch_dvma_attr.dlim_cntr_max = 0xffffffff; ch_dvma_attr.dlim_burstsizes = 0xfff; ch_dvma_attr.dlim_minxfer = 1; ch_dvma_attr.dlim_dmaspeed = 0; rv = dvma_reserve( chp->ch_dip, /* device dev_info */ &ch_dvma_attr, /* DVMA attributes */ 3, /* number of pages */ &ch_dh); /* DVMA handle */ if (rv != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ch_get_dvma_handle: dvma_reserve() error %d\n", chp->ch_name, rv); kmem_free(dhe, sizeof (*dhe)); return ((free_dh_t *)0); } dhe->dhe_dh = (ulong_t)ch_dh; return (dhe); } /* * free the linked list of dvma descriptor entries. * DVMA is only on sparc! */ static void ch_free_dvma_handles(ch_t *chp) { free_dh_t *dhe, *the; dhe = chp->ch_vdh; while (dhe) { dvma_release((ddi_dma_handle_t)dhe->dhe_dh); the = dhe; dhe = dhe->dhe_next; kmem_free(the, sizeof (*the)); } chp->ch_vdh = NULL; } /* * ch_bind_dvma_handle() * * returns # of entries used off of cmdQ_ce_t array to hold physical addrs. * DVMA in sparc only * * chp - per-board descriptor * size - # bytes mapped * vaddr - virtual address * cmp - array of cmdQ_ce_t entries * cnt - # free entries in cmp array */ uint32_t ch_bind_dvma_handle(ch_t *chp, int size, caddr_t vaddr, cmdQ_ce_t *cmp, uint32_t cnt) { ddi_dma_cookie_t cookie; ddi_dma_handle_t ch_dh; uint32_t n = 1; free_dh_t *dhe; mutex_enter(&chp->ch_dh_lck); if ((dhe = chp->ch_vdh) != NULL) { chp->ch_vdh = dhe->dhe_next; } mutex_exit(&chp->ch_dh_lck); if (dhe == NULL) { return (0); } ch_dh = (ddi_dma_handle_t)dhe->dhe_dh; n = cnt; dvma_kaddr_load( ch_dh, /* dvma handle */ vaddr, /* virtual address */ size, /* length of object */ 0, /* start at index 0 */ &cookie); dvma_sync(ch_dh, 0, DDI_DMA_SYNC_FORDEV); cookie.dmac_notused = 0; n = 1; cmp->ce_pa = cookie.dmac_laddress; cmp->ce_dh = dhe; cmp->ce_len = cookie.dmac_size; cmp->ce_mp = NULL; cmp->ce_flg = DH_DVMA; /* indicate a dvma descriptor */ return (n); } /* * ch_unbind_dvma_handle() * * frees resources alloacted by ch_bind_dvma_handle(). * * frees DMA handle */ void ch_unbind_dvma_handle(ch_t *chp, free_dh_t *dhe) { ddi_dma_handle_t ch_dh = (ddi_dma_handle_t)dhe->dhe_dh; dvma_unload(ch_dh, 0, -1); mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_vdh; chp->ch_vdh = dhe; mutex_exit(&chp->ch_dh_lck); } #endif /* defined(__sparc) */ /* * send received packet up stream. * * if driver has been stopped, then we drop the message. */ void ch_send_up(ch_t *chp, mblk_t *mp, uint32_t cksum, int flg) { /* * probably do not need a lock here. When we set PESTOP in * ch_stop() a packet could have just passed here and gone * upstream. The next one will be dropped. */ if (chp->ch_state == PERUNNING) { /* * note that flg will not be set unless enable_checksum_offload * set in /etc/system (see sge.c). */ if (flg) mac_hcksum_set(mp, 0, 0, 0, cksum, HCK_FULLCKSUM); gld_recv(chp->ch_macp, mp); } else { freemsg(mp); } } /* * unblock gld driver. */ void ch_gld_ok(ch_t *chp) { gld_sched(chp->ch_macp); } /* * reset the card. * * Note: we only do this after the card has been initialized. */ static int ch_reset(gld_mac_info_t *mp) { ch_t *chp; if (mp == NULL) { return (GLD_FAILURE); } chp = (ch_t *)mp->gldm_private; if (chp == NULL) { return (GLD_FAILURE); } #ifdef NOTYET /* * do a reset of card * * 1. set PwrState to D3hot (3) * 2. clear PwrState flags */ /* * When we did this, the card didn't start. First guess is that * the initialization is not quite correct. For now, we don't * reset things. */ if (chp->ch_hpci) { pci_config_put32(chp->ch_hpci, 0x44, 3); pci_config_put32(chp->ch_hpci, 0x44, 0); /* delay .5 sec */ DELAY(500000); } #endif return (GLD_SUCCESS); } static int ch_start(gld_mac_info_t *macinfo) { ch_t *chp = (ch_t *)macinfo->gldm_private; #ifdef CONFIG_CHELSIO_T1_OFFLOAD /* only initialize card on first attempt */ mutex_enter(&chp->ch_lock); chp->ch_refcnt++; if (chp->ch_refcnt == 1) { chp->ch_state = PERUNNING; mutex_exit(&chp->ch_lock); pe_init((void *)chp); } else mutex_exit(&chp->ch_lock); #else pe_init((void *)chp); /* go to running state, we're being started */ mutex_enter(&chp->ch_lock); chp->ch_state = PERUNNING; mutex_exit(&chp->ch_lock); #endif return (GLD_SUCCESS); } static int ch_stop(gld_mac_info_t *mp) { ch_t *chp = (ch_t *)mp->gldm_private; /* * can only stop the chip if it's been initialized */ mutex_enter(&chp->ch_lock); if (chp->ch_state == PEIDLE) { mutex_exit(&chp->ch_lock); return (GLD_FAILURE); } #ifdef CONFIG_CHELSIO_T1_OFFLOAD chp->ch_refcnt--; if (chp->ch_refcnt == 0) { chp->ch_state = PESTOP; mutex_exit(&chp->ch_lock); pe_stop(chp); } else mutex_exit(&chp->ch_lock); #else chp->ch_state = PESTOP; mutex_exit(&chp->ch_lock); pe_stop(chp); #endif return (GLD_SUCCESS); } static int ch_set_mac_address(gld_mac_info_t *mp, uint8_t *mac) { ch_t *chp; if (mp) { chp = (ch_t *)mp->gldm_private; } else { return (GLD_FAILURE); } pe_set_mac(chp, mac); return (GLD_SUCCESS); } static int ch_set_multicast(gld_mac_info_t *mp, uint8_t *ep, int flg) { ch_t *chp = (ch_t *)mp->gldm_private; return (pe_set_mc(chp, ep, flg)); } static int ch_ioctl(gld_mac_info_t *macinfo, queue_t *q, mblk_t *mp) { struct iocblk *iocp; switch (mp->b_datap->db_type) { case M_IOCTL: /* pe_ioctl() does qreply() */ pe_ioctl((ch_t *)(macinfo->gldm_private), q, mp); break; default: /* * cmn_err(CE_NOTE, "ch_ioctl not M_IOCTL\n"); * debug_enter("bad ch_ioctl"); */ iocp = (struct iocblk *)mp->b_rptr; if (mp->b_cont) freemsg(mp->b_cont); mp->b_cont = NULL; mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = EINVAL; qreply(q, mp); break; } return (GLD_SUCCESS); } static int ch_set_promiscuous(gld_mac_info_t *mp, int flag) { ch_t *chp = (ch_t *)mp->gldm_private; switch (flag) { case GLD_MAC_PROMISC_MULTI: pe_set_promiscuous(chp, 2); break; case GLD_MAC_PROMISC_NONE: pe_set_promiscuous(chp, 0); break; case GLD_MAC_PROMISC_PHYS: default: pe_set_promiscuous(chp, 1); break; } return (GLD_SUCCESS); } static int ch_get_stats(gld_mac_info_t *mp, struct gld_stats *gs) { ch_t *chp = (ch_t *)mp->gldm_private; uint64_t speed; uint32_t intrcnt; uint32_t norcvbuf; uint32_t oerrors; uint32_t ierrors; uint32_t underrun; uint32_t overrun; uint32_t framing; uint32_t crc; uint32_t carrier; uint32_t collisions; uint32_t xcollisions; uint32_t late; uint32_t defer; uint32_t xerrs; uint32_t rerrs; uint32_t toolong; uint32_t runt; ulong_t multixmt; ulong_t multircv; ulong_t brdcstxmt; ulong_t brdcstrcv; /* * race looks benign here. */ if (chp->ch_state != PERUNNING) { return (GLD_FAILURE); } (void) pe_get_stats(chp, &speed, &intrcnt, &norcvbuf, &oerrors, &ierrors, &underrun, &overrun, &framing, &crc, &carrier, &collisions, &xcollisions, &late, &defer, &xerrs, &rerrs, &toolong, &runt, &multixmt, &multircv, &brdcstxmt, &brdcstrcv); gs->glds_speed = speed; gs->glds_media = GLDM_UNKNOWN; gs->glds_intr = intrcnt; gs->glds_norcvbuf = norcvbuf; gs->glds_errxmt = oerrors; gs->glds_errrcv = ierrors; gs->glds_missed = ierrors; /* ??? */ gs->glds_underflow = underrun; gs->glds_overflow = overrun; gs->glds_frame = framing; gs->glds_crc = crc; gs->glds_duplex = GLD_DUPLEX_FULL; gs->glds_nocarrier = carrier; gs->glds_collisions = collisions; gs->glds_excoll = xcollisions; gs->glds_xmtlatecoll = late; gs->glds_defer = defer; gs->glds_dot3_first_coll = 0; /* Not available */ gs->glds_dot3_multi_coll = 0; /* Not available */ gs->glds_dot3_sqe_error = 0; /* Not available */ gs->glds_dot3_mac_xmt_error = xerrs; gs->glds_dot3_mac_rcv_error = rerrs; gs->glds_dot3_frame_too_long = toolong; gs->glds_short = runt; gs->glds_noxmtbuf = 0; /* not documented */ gs->glds_xmtretry = 0; /* not documented */ gs->glds_multixmt = multixmt; /* not documented */ gs->glds_multircv = multircv; /* not documented */ gs->glds_brdcstxmt = brdcstxmt; /* not documented */ gs->glds_brdcstrcv = brdcstrcv; /* not documented */ return (GLD_SUCCESS); } static int ch_send(gld_mac_info_t *macinfo, mblk_t *mp) { ch_t *chp = (ch_t *)macinfo->gldm_private; uint32_t flg; uint32_t msg_flg; #ifdef TX_CKSUM_FIX mblk_t *nmp; int frags; size_t msg_len; struct ether_header *ehdr; ipha_t *ihdr; int tflg = 0; #endif /* TX_CKSUM_FIX */ /* * race looks benign here. */ if (chp->ch_state != PERUNNING) { return (GLD_FAILURE); } msg_flg = 0; if (chp->ch_config.cksum_enabled) { if (is_T2(chp)) { mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &msg_flg); flg = (msg_flg & HCK_FULLCKSUM)? CH_NO_CPL: CH_NO_HWCKSUM|CH_NO_CPL; } else flg = CH_NO_CPL; } else flg = CH_NO_HWCKSUM | CH_NO_CPL; #ifdef TX_CKSUM_FIX /* * Check if the message spans more than one mblk or * if it does and the ip header is not in the first * fragment then pull up the message. This case is * expected to be rare. */ frags = 0; msg_len = 0; nmp = mp; do { frags++; msg_len += MBLKL(nmp); nmp = nmp->b_cont; } while (nmp); #define MAX_ALL_HDRLEN SZ_CPL_TX_PKT + sizeof (struct ether_header) + \ TCP_MAX_COMBINED_HEADER_LENGTH /* * If the first mblk has enough space at the beginning of * the data buffer to hold a CPL header, then, we'll expancd * the front of the buffer so a pullup will leave space for * pe_start() to add the CPL header in line. We need to remember * that we've done this so we can undo it after the pullup. * * Note that if we decide to do an allocb to hold the CPL header, * we need to catch the case where we've added an empty mblk for * the header but never did a pullup. This would result in the * tests for etherheader, etc. being done on the initial, empty, * mblk instead of the one with data. See PR3646 for further * details. (note this PR is closed since it is no longer relevant). * * Another point is that if we do add an allocb to add space for * a CPL header, after a pullup, the initial pointer, mp, in GLD will * no longer point to a valid mblk. When we get the mblk (by allocb), * we need to switch the mblk structure values between it and the * mp structure values referenced by GLD. This handles the case where * we've run out of cmdQ entries and report GLD_NORESOURCES back to * GLD. The pointer to the mblk data will have been modified to hold * an empty 8 bytes for the CPL header, For now, we let the pe_start() * routine prepend an 8 byte mblk. */ if (MBLKHEAD(mp) >= SZ_CPL_TX_PKT) { mp->b_rptr -= SZ_CPL_TX_PKT; tflg = 1; } if (frags > 3) { chp->sge->intr_cnt.tx_msg_pullups++; if (pullupmsg(mp, -1) == 0) { freemsg(mp); return (GLD_SUCCESS); } } else if ((msg_len > MAX_ALL_HDRLEN) && (MBLKL(mp) < MAX_ALL_HDRLEN)) { chp->sge->intr_cnt.tx_hdr_pullups++; if (pullupmsg(mp, MAX_ALL_HDRLEN) == 0) { freemsg(mp); return (GLD_SUCCESS); } } if (tflg) mp->b_rptr += SZ_CPL_TX_PKT; ehdr = (struct ether_header *)mp->b_rptr; if (ehdr->ether_type == htons(ETHERTYPE_IP)) { ihdr = (ipha_t *)&mp->b_rptr[sizeof (struct ether_header)]; if ((ihdr->ipha_fragment_offset_and_flags & IPH_MF)) { if (ihdr->ipha_protocol == IPPROTO_UDP) { flg |= CH_UDP_MF; chp->sge->intr_cnt.tx_udp_ip_frag++; } else if (ihdr->ipha_protocol == IPPROTO_TCP) { flg |= CH_TCP_MF; chp->sge->intr_cnt.tx_tcp_ip_frag++; } } else if (ihdr->ipha_protocol == IPPROTO_UDP) flg |= CH_UDP; } #endif /* TX_CKSUM_FIX */ /* * return 0 - data send successfully * return 1 - no resources, reschedule */ if (pe_start(chp, mp, flg)) return (GLD_NORESOURCES); else return (GLD_SUCCESS); } static uint_t ch_intr(gld_mac_info_t *mp) { return (pe_intr((ch_t *)mp->gldm_private)); } /* * generate name of driver with unit# postpended. */ void ch_set_name(ch_t *chp, int unit) { chp->ch_name = (char *)kmem_alloc(sizeof ("chxge00"), KM_SLEEP); if (unit > 9) { bcopy("chxge00", (void *)chp->ch_name, sizeof ("chxge00")); chp->ch_name[5] += unit/10; chp->ch_name[6] += unit%10; } else { bcopy("chxge0", (void *)chp->ch_name, sizeof ("chxge0")); chp->ch_name[5] += unit; } } void ch_free_name(ch_t *chp) { if (chp->ch_name) kmem_free(chp->ch_name, sizeof ("chxge00")); chp->ch_name = NULL; } #ifdef CONFIG_CHELSIO_T1_OFFLOAD /* * register toe offload. */ void * ch_register(void *instp, void *toe_rcv, void *toe_free, void *toe_tunnel, kmutex_t *toe_tx_mx, kcondvar_t *toe_of_cv, int unit) { ch_t *chp = gchp[unit]; if (chp != NULL) { mutex_enter(&chp->ch_lock); chp->toe_rcv = (void (*)(void *, mblk_t *))toe_rcv; chp->ch_toeinst = instp; chp->toe_free = (void (*)(void *, tbuf_t *))toe_free; chp->toe_tunnel = (int (*)(void *, mblk_t *))toe_tunnel; chp->ch_tx_overflow_mutex = toe_tx_mx; chp->ch_tx_overflow_cv = toe_of_cv; chp->open_device_map |= TOEDEV_DEVMAP_BIT; /* start up adapter if first user */ chp->ch_refcnt++; if (chp->ch_refcnt == 1) { chp->ch_state = PERUNNING; mutex_exit(&chp->ch_lock); pe_init((void *)chp); } else mutex_exit(&chp->ch_lock); } return ((void *)gchp[unit]); } /* * unregister toe offload. * XXX Need to fix races here. * 1. turn off SGE interrupts. * 2. do update * 3. re-enable SGE interrupts * 4. SGE doorbell to make sure things get restarted. */ void ch_unregister(void) { int i; ch_t *chp; for (i = 0; i < MAX_CARDS; i++) { chp = gchp[i]; if (chp == NULL) continue; mutex_enter(&chp->ch_lock); chp->ch_refcnt--; if (chp->ch_refcnt == 0) { chp->ch_state = PESTOP; mutex_exit(&chp->ch_lock); pe_stop(chp); } else mutex_exit(&chp->ch_lock); chp->open_device_map &= ~TOEDEV_DEVMAP_BIT; chp->toe_rcv = NULL; chp->ch_toeinst = NULL; chp->toe_free = NULL; chp->toe_tunnel = NULL; chp->ch_tx_overflow_mutex = NULL; chp->ch_tx_overflow_cv = NULL; } } #endif /* CONFIG_CHELSIO_T1_OFFLOAD */ /* * get properties from chxge.conf */ static void ch_get_prop(ch_t *chp) { int val; int tval = 0; extern int enable_latency_timer; extern uint32_t sge_cmdq0_cnt; extern uint32_t sge_cmdq1_cnt; extern uint32_t sge_flq0_cnt; extern uint32_t sge_flq1_cnt; extern uint32_t sge_respq_cnt; extern uint32_t sge_cmdq0_cnt_orig; extern uint32_t sge_cmdq1_cnt_orig; extern uint32_t sge_flq0_cnt_orig; extern uint32_t sge_flq1_cnt_orig; extern uint32_t sge_respq_cnt_orig; dev_info_t *pdip; uint32_t vendor_id, device_id, revision_id; uint32_t *prop_val = NULL; uint32_t prop_len = 0; val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable_dvma", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable-dvma", -1); if (val != -1) { if (val != 0) chp->ch_config.enable_dvma = 1; } val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "amd_bug_workaround", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "amd-bug-workaround", -1); if (val != -1) { if (val == 0) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } } /* * Step up to the parent node, That's the node above us * in the device tree. And will typically be the PCI host * Controller. */ pdip = ddi_get_parent(chp->ch_dip); /* * Now get the 'Vendor id' properties */ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pdip, 0, "vendor-id", (int **)&prop_val, &prop_len) != DDI_PROP_SUCCESS) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } vendor_id = *(uint32_t *)prop_val; ddi_prop_free(prop_val); /* * Now get the 'Device id' properties */ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pdip, 0, "device-id", (int **)&prop_val, &prop_len) != DDI_PROP_SUCCESS) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } device_id = *(uint32_t *)prop_val; ddi_prop_free(prop_val); /* * Now get the 'Revision id' properties */ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pdip, 0, "revision-id", (int **)&prop_val, &prop_len) != DDI_PROP_SUCCESS) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } revision_id = *(uint32_t *)prop_val; ddi_prop_free(prop_val); /* * set default values based on node above us. */ if ((vendor_id == AMD_VENDOR_ID) && (device_id == AMD_BRIDGE) && (revision_id <= AMD_BRIDGE_REV)) { uint32_t v; uint32_t burst; uint32_t cnt; /* if 133 Mhz not enabled, then do nothing - we're not PCIx */ v = pci_config_get32(chp->ch_hpci, 0x64); if ((v & 0x20000) == 0) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } /* check burst size and transaction count */ v = pci_config_get32(chp->ch_hpci, 0x60); burst = (v >> 18) & 3; cnt = (v >> 20) & 7; switch (burst) { case 0: /* 512 */ /* 512 burst size legal with split cnts 1,2,3 */ if (cnt <= 2) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } break; case 1: /* 1024 */ /* 1024 burst size legal with split cnts 1,2 */ if (cnt <= 1) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } break; case 2: /* 2048 */ /* 2048 burst size legal with split cnts 1 */ if (cnt == 0) { chp->ch_config.burstsize_set = 0; chp->ch_config.transaction_cnt_set = 0; goto fail_exit; } break; case 3: /* 4096 */ break; } } else { goto fail_exit; } /* * if illegal burst size seen, then default to 1024 burst size */ chp->ch_config.burstsize = 1; chp->ch_config.burstsize_set = 1; /* * if illegal transaction cnt seen, then default to 2 */ chp->ch_config.transaction_cnt = 1; chp->ch_config.transaction_cnt_set = 1; fail_exit: /* * alter the burstsize parameter via an entry * in chxge.conf */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci_burstsize", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci-burstsize", -1); if (val != -1) { switch (val) { case 0: /* use default */ chp->ch_config.burstsize_set = 0; break; case 1024: chp->ch_config.burstsize_set = 1; chp->ch_config.burstsize = 1; break; case 2048: chp->ch_config.burstsize_set = 1; chp->ch_config.burstsize = 2; break; case 4096: cmn_err(CE_WARN, "%s not supported %d\n", chp->ch_name, val); break; default: cmn_err(CE_WARN, "%s illegal burst size %d\n", chp->ch_name, val); break; } } /* * set transaction count */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci_split_transaction_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci-split-transaction-cnt", -1); if (val != -1) { switch (val) { case 0: /* use default */ chp->ch_config.transaction_cnt_set = 0; break; case 1: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 0; break; case 2: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 1; break; case 3: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 2; break; case 4: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 3; break; case 8: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 4; break; case 12: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 5; break; case 16: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 6; break; case 32: chp->ch_config.transaction_cnt_set = 1; chp->ch_config.transaction_cnt = 7; break; default: cmn_err(CE_WARN, "%s illegal transaction cnt %d\n", chp->ch_name, val); break; } } /* * set relaxed ordering bit? */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci_relaxed_ordering_on", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "pci-relaxed-ordering-on", -1); /* * default is to use system default value. */ chp->ch_config.relaxed_ordering = 0; if (val != -1) { if (val) chp->ch_config.relaxed_ordering = 1; } val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable_latency_timer", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable-latency-timer", -1); if (val != -1) enable_latency_timer = (val == 0)? 0: 1; /* * default maximum Jumbo Frame size. */ chp->ch_maximum_mtu = 9198; /* tunable via chxge.conf */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "maximum_mtu", -1); if (val == -1) { val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "maximum-mtu", -1); } if (val != -1) { if (val > 9582) { cmn_err(CE_WARN, "maximum_mtu value %d > 9582. Value set to 9582", val); val = 9582; } else if (val < 1500) { cmn_err(CE_WARN, "maximum_mtu value %d < 1500. Value set to 1500", val); val = 1500; } if (val) chp->ch_maximum_mtu = val; } /* * default value for this instance mtu */ chp->ch_mtu = ETHERMTU; val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "accept_jumbo", -1); if (val == -1) { val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "accept-jumbo", -1); } if (val != -1) { if (val) chp->ch_mtu = chp->ch_maximum_mtu; } #ifdef CONFIG_CHELSIO_T1_OFFLOAD chp->ch_sm_buf_sz = 0x800; chp->ch_sm_buf_aln = 0x800; chp->ch_bg_buf_sz = 0x4000; chp->ch_bg_buf_aln = 0x4000; #else chp->ch_sm_buf_sz = 0x200; chp->ch_sm_buf_aln = 0x200; chp->ch_bg_buf_sz = 0x800; chp->ch_bg_buf_aln = 0x800; if ((chp->ch_mtu > 0x800) && (chp->ch_mtu <= 0x1000)) { chp->ch_sm_buf_sz = 0x400; chp->ch_sm_buf_aln = 0x400; chp->ch_bg_buf_sz = 0x1000; chp->ch_bg_buf_aln = 0x1000; } else if ((chp->ch_mtu > 0x1000) && (chp->ch_mtu <= 0x2000)) { chp->ch_sm_buf_sz = 0x400; chp->ch_sm_buf_aln = 0x400; chp->ch_bg_buf_sz = 0x2000; chp->ch_bg_buf_aln = 0x2000; } else if (chp->ch_mtu > 0x2000) { chp->ch_sm_buf_sz = 0x400; chp->ch_sm_buf_aln = 0x400; chp->ch_bg_buf_sz = 0x3000; chp->ch_bg_buf_aln = 0x4000; } #endif chp->ch_config.cksum_enabled = 1; val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable_checksum_offload", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "enable-checksum-offload", -1); if (val != -1) { if (val == 0) chp->ch_config.cksum_enabled = 0; } /* * Provides a tuning capability for the command queue 0 size. */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge_cmdq0_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge-cmdq0-cnt", -1); if (val != -1) { if (val > 10) sge_cmdq0_cnt = val; } if (sge_cmdq0_cnt > 65535) { cmn_err(CE_WARN, "%s: sge-cmdQ0-cnt > 65535 - resetting value to default", chp->ch_name); sge_cmdq0_cnt = sge_cmdq0_cnt_orig; } tval += sge_cmdq0_cnt; /* * Provides a tuning capability for the command queue 1 size. */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge_cmdq1_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge-cmdq1-cnt", -1); if (val != -1) { if (val > 10) sge_cmdq1_cnt = val; } if (sge_cmdq1_cnt > 65535) { cmn_err(CE_WARN, "%s: sge-cmdQ0-cnt > 65535 - resetting value to default", chp->ch_name); sge_cmdq1_cnt = sge_cmdq1_cnt_orig; } /* * Provides a tuning capability for the free list 0 size. */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge_flq0_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge-flq0-cnt", -1); if (val != -1) { if (val > 512) sge_flq0_cnt = val; } if (sge_flq0_cnt > 65535) { cmn_err(CE_WARN, "%s: sge-flq0-cnt > 65535 - resetting value to default", chp->ch_name); sge_flq0_cnt = sge_flq0_cnt_orig; } tval += sge_flq0_cnt; /* * Provides a tuning capability for the free list 1 size. */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge_flq1_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge-flq1-cnt", -1); if (val != -1) { if (val > 512) sge_flq1_cnt = val; } if (sge_flq1_cnt > 65535) { cmn_err(CE_WARN, "%s: sge-flq1-cnt > 65535 - resetting value to default", chp->ch_name); sge_flq1_cnt = sge_flq1_cnt_orig; } tval += sge_flq1_cnt; /* * Provides a tuning capability for the responce queue size. */ val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge_respq_cnt", -1); if (val == -1) val = ddi_getprop(DDI_DEV_T_ANY, chp->ch_dip, DDI_PROP_DONTPASS, "sge-respq-cnt", -1); if (val != -1) { if (val > 30) sge_respq_cnt = val; } if (sge_respq_cnt > 65535) { cmn_err(CE_WARN, "%s: sge-respq-cnt > 65535 - resetting value to default", chp->ch_name); sge_respq_cnt = sge_respq_cnt_orig; } if (tval > sge_respq_cnt) { if (tval <= 65535) { cmn_err(CE_WARN, "%s: sge-respq-cnt < %d - setting value to %d (cmdQ+flq0+flq1)", chp->ch_name, tval, tval); sge_respq_cnt = tval; } else { cmn_err(CE_WARN, "%s: Q sizes invalid - resetting to default values", chp->ch_name); sge_cmdq0_cnt = sge_cmdq0_cnt_orig; sge_cmdq1_cnt = sge_cmdq1_cnt_orig; sge_flq0_cnt = sge_flq0_cnt_orig; sge_flq1_cnt = sge_flq1_cnt_orig; sge_respq_cnt = sge_respq_cnt_orig; } } }