/* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file is part of the Chelsio T1 Ethernet driver. * * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved. */ /* * Solaris Multithreaded STREAMS Chelsio PCI Ethernet Driver. * Interface code */ #include <sys/types.h> #include <sys/systm.h> #include <sys/cmn_err.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/byteorder.h> #include <sys/atomic.h> #include <sys/ethernet.h> #if PE_PROFILING_ENABLED #include <sys/time.h> #endif #include <sys/gld.h> #include "ostypes.h" #include "common.h" #include "oschtoe.h" #ifdef CONFIG_CHELSIO_T1_1G #include "fpga_defs.h" #endif #include "regs.h" #ifdef CONFIG_CHELSIO_T1_OFFLOAD #include "mc3.h" #include "mc4.h" #endif #include "sge.h" #include "tp.h" #ifdef CONFIG_CHELSIO_T1_OFFLOAD #include "ulp.h" #endif #include "espi.h" #include "elmer0.h" #include "gmac.h" #include "cphy.h" #include "suni1x10gexp_regs.h" #include "ch.h" #define MLEN(mp) ((mp)->b_wptr - (mp)->b_rptr) extern uint32_t buffers_in_use[]; extern kmutex_t in_use_l; extern uint32_t in_use_index; static void link_start(ch_t *sa, struct pe_port_t *pp); static ch_esb_t *ch_alloc_small_esbbuf(ch_t *sa, uint32_t i); static ch_esb_t *ch_alloc_big_esbbuf(ch_t *sa, uint32_t i); void ch_big_rbuf_recycle(ch_esb_t *rbp); void ch_small_rbuf_recycle(ch_esb_t *rbp); static const struct board_info *pe_sa_init(ch_t *sa); static int ch_set_config_data(ch_t *chp); void pe_rbuf_pool_free(ch_t *chp); static void pe_free_driver_resources(ch_t *sa); static void update_mtu_tab(ch_t *adapter); static int pe_change_mtu(ch_t *chp); /* * CPL5 Defines (from netinet/cpl5_commands.h) */ #define FLITSTOBYTES 8 #define CPL_FORMAT_0_SIZE 8 #define CPL_FORMAT_1_SIZE 16 #define CPL_FORMAT_2_SIZE 24 #define CPL_FORMAT_3_SIZE 32 #define CPL_FORMAT_4_SIZE 40 #define CPL_FORMAT_5_SIZE 48 #define TID_MASK 0xffffff #define PE_LINK_SPEED_AUTONEG 5 static int pe_small_rbuf_pool_init(ch_t *sa); static int pe_big_rbuf_pool_init(ch_t *sa); static int pe_make_fake_arp(ch_t *chp, unsigned char *arpp); static uint32_t pe_get_ip(unsigned char *arpp); /* * May be set in /etc/system to 0 to use default latency timer for 10G. * See PCI register 0xc definition. */ int enable_latency_timer = 1; /* * May be set in /etc/system to 0 to disable hardware checksum for * TCP and UDP. */ int enable_checksum_offload = 1; /* * Multiplier for freelist pool. */ int fl_sz_multiplier = 6; uint_t pe_intr(ch_t *sa) { mutex_enter(&sa->ch_intr); if (sge_data_in(sa->sge)) { sa->isr_intr++; mutex_exit(&sa->ch_intr); return (DDI_INTR_CLAIMED); } mutex_exit(&sa->ch_intr); return (DDI_INTR_UNCLAIMED); } /* * Each setup struct will call this function to * initialize. */ void pe_init(void* xsa) { ch_t *sa = NULL; int i = 0; sa = (ch_t *)xsa; /* * Need to count the number of times this routine is called * because we only want the resources to be allocated once. * The 7500 has four ports and so this routine can be called * once for each port. */ if (sa->init_counter == 0) { for_each_port(sa, i) { /* * We only want to initialize the line if it is down. */ if (sa->port[i].line_up == 0) { link_start(sa, &sa->port[i]); sa->port[i].line_up = 1; } } (void) t1_init_hw_modules(sa); /* * Enable/Disable checksum offloading. */ if (sa->ch_config.cksum_enabled) { if (sa->config_data.offload_ip_cksum) { /* Notify that HW will do the checksum. */ t1_tp_set_ip_checksum_offload(sa->tp, 1); } if (sa->config_data.offload_tcp_cksum) { /* Notify that HW will do the checksum. */ t1_tp_set_tcp_checksum_offload(sa->tp, 1); } if (sa->config_data.offload_udp_cksum) { /* Notify that HW will do the checksum. */ t1_tp_set_udp_checksum_offload(sa->tp, 1); } } sa->ch_flags |= PEINITDONE; sa->init_counter++; } /* * Enable interrupts after starting the SGE so * that the SGE is ready to handle interrupts. */ (void) sge_start(sa->sge); t1_interrupts_enable(sa); /* * set mtu (either 1500 or bigger) */ (void) pe_change_mtu(sa); #ifdef HOST_PAUSE /* * get the configured value of the MAC. */ (void) t1_tpi_read(sa, SUNI1x10GEXP_REG_TXXG_CONFIG_1 << 2, &sa->txxg_cfg1); #endif } /* ARGSUSED */ static void link_start(ch_t *sa, struct pe_port_t *p) { struct cmac *mac = p->mac; mac->ops->reset(mac); if (mac->ops->macaddress_set) mac->ops->macaddress_set(mac, p->enaddr); (void) t1_link_start(p->phy, mac, &p->link_config); mac->ops->enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); } /* * turn off interrupts... */ void pe_stop(ch_t *sa) { t1_interrupts_disable(sa); (void) sge_stop(sa->sge); /* * we can still be running an interrupt thread in sge_data_in(). * If we are, we'll block on the ch_intr lock */ mutex_enter(&sa->ch_intr); mutex_exit(&sa->ch_intr); } /* * output mblk to SGE level and out to the wire. */ int pe_start(ch_t *sa, mblk_t *mp, uint32_t flg) { mblk_t *m0 = mp; cmdQ_ce_t cm[16]; cmdQ_ce_t *cmp; cmdQ_ce_t *hmp = &cm[0]; /* head of cm table (may be kmem_alloed) */ int cm_flg = 0; /* flag (1 - if kmem-alloced) */ int nseg = 0; /* number cmdQ_ce entries created */ int mseg = 16; /* maximum entries in hmp arrary */ int freeme = 0; /* we have an mblk to free in case of error */ uint32_t ch_bind_dma_handle(ch_t *, int, caddr_t, cmdQ_ce_t *, uint32_t); #if defined(__sparc) uint32_t ch_bind_dvma_handle(ch_t *, int, caddr_t, cmdQ_ce_t *, uint32_t); #endif int rv; /* return value on error */ #ifdef CONFIG_CHELSIO_T1_OFFLOAD if (flg & CH_OFFLOAD) { hmp->ce_pa = ((tbuf_t *)mp)->tb_pa; hmp->ce_dh = NULL; hmp->ce_flg = DH_TOE; hmp->ce_len = ((tbuf_t *)mp)->tb_len; hmp->ce_mp = mp; /* make sure data is flushed to physical memory */ (void) ddi_dma_sync((ddi_dma_handle_t)((tbuf_t *)mp)->tb_dh, (off_t)0, hmp->ce_len, DDI_DMA_SYNC_FORDEV); if (sge_data_out(sa->sge, 0, mp, hmp, 1, flg) == 0) { return (0); } /* * set a flag so we'll restart upper layer when * resources become available. */ sa->ch_blked = 1; return (1); } #endif /* CONFIG_CHELSIO_T1_OFFLOAD */ /* writes from toe will always have CPL header in place */ if (flg & CH_NO_CPL) { struct cpl_tx_pkt *cpl; /* PR2928 & PR3309 */ if (sa->ch_ip == NULL) { ushort_t ethertype = ntohs(*(short *)&mp->b_rptr[12]); if (ethertype == ETHERTYPE_ARP) { if (is_T2(sa)) { /* * We assume here that the arp will be * contained in one mblk. */ if (pe_make_fake_arp(sa, mp->b_rptr)) { freemsg(mp); sa->oerr++; return (0); } } else { sa->ch_ip = pe_get_ip(mp->b_rptr); } } } /* * if space in front of packet big enough for CPL * header, then use it. We'll allocate an mblk * otherwise. */ if ((mp->b_rptr - mp->b_datap->db_base) >= SZ_CPL_TX_PKT) { mp->b_rptr -= SZ_CPL_TX_PKT; } else { #ifdef SUN_KSTATS sa->sge->intr_cnt.tx_need_cpl_space++; #endif m0 = allocb(SZ_CPL_TX_PKT, BPRI_HI); if (m0 == NULL) { freemsg(mp); sa->oerr++; return (0); } m0->b_wptr = m0->b_rptr + SZ_CPL_TX_PKT; m0->b_cont = mp; freeme = 1; mp = m0; } /* fill in cpl header */ cpl = (struct cpl_tx_pkt *)mp->b_rptr; cpl->opcode = CPL_TX_PKT; cpl->iff = 0; /* XXX port 0 needs fixing with NEMO */ cpl->ip_csum_dis = 1; /* no IP header cksum */ cpl->l4_csum_dis = flg & CH_NO_HWCKSUM; /* CH_NO_HWCKSUM == 1 */ cpl->vlan_valid = 0; /* no vlan */ } if (m0->b_cont) { #ifdef SUN_KSTATS sa->sge->intr_cnt.tx_multi_mblks++; #endif while (mp) { int lseg; /* added by ch_bind_dma_handle() */ int len; len = MLEN(mp); /* skip mlks with no data */ if (len == 0) { mp = mp->b_cont; continue; } /* * if we've run out of space on stack, then we * allocate a temporary buffer to hold the * information. This will kill the the performance, * but since it shouldn't really occur, we can live * with it. Since jumbo frames may map multiple * descriptors, we reallocate the hmp[] array before * we reach the end. */ if (nseg >= (mseg-4)) { cmdQ_ce_t *buf; int j; buf = kmem_alloc(sizeof (cmdQ_ce_t) * 2 * mseg, KM_SLEEP); for (j = 0; j < nseg; j++) buf[j] = hmp[j]; if (cm_flg) { kmem_free(hmp, mseg * sizeof (cmdQ_ce_t)); } else cm_flg = 1; hmp = buf; mseg = 2*mseg; /* * We've used up ch table on stack */ } #if defined(__sparc) if (sa->ch_config.enable_dvma) { lseg = ch_bind_dvma_handle(sa, len, (void *)mp->b_rptr, &hmp[nseg], mseg - nseg); if (lseg == NULL) { sa->sge->intr_cnt.tx_no_dvma1++; if ((lseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[nseg], mseg - nseg)) == NULL) { sa->sge->intr_cnt.tx_no_dma1++; /* * ran out of space. Gonna bale */ rv = 0; /* * we may have processed * previous mblks and have * descriptors. If so, we need * to free the meta struct * entries before freeing * the mblk. */ if (nseg) goto error; goto error1; } } } else { lseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[nseg], mseg - nseg); if (lseg == NULL) { sa->sge->intr_cnt.tx_no_dma1++; /* * ran out of space. Gona bale */ rv = 0; /* * we may have processed previous * mblks and have descriptors. If so, * we need to free the meta struct * entries before freeing the mblk. */ if (nseg) goto error; goto error1; } } #else /* defined(__sparc) */ lseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[nseg], mseg - nseg); if (lseg == NULL) { sa->sge->intr_cnt.tx_no_dma1++; /* * ran out of space. Gona bale */ rv = 0; /* * we may have processed previous mblks and * have descriptors. If so, we need to free * the meta struct entries before freeing * the mblk. */ if (nseg) goto error; goto error1; } #endif /* defined(__sparc) */ nseg += lseg; mp = mp->b_cont; } /* * SHOULD NEVER OCCUR, BUT... * no data if nseg 0 or * nseg 1 and a CPL mblk (CPL mblk only with offload mode) * and no data */ if ((nseg == 0) || (freeme && (nseg == 1))) { rv = 0; goto error1; } } else { int len; /* we assume that we always have data with one packet */ len = MLEN(mp); #if defined(__sparc) if (sa->ch_config.enable_dvma) { nseg = ch_bind_dvma_handle(sa, len, (void *)mp->b_rptr, &hmp[0], 16); if (nseg == NULL) { sa->sge->intr_cnt.tx_no_dvma2++; nseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[0], 16); if (nseg == NULL) { sa->sge->intr_cnt.tx_no_dma2++; /* * ran out of space. Gona bale */ rv = 0; goto error1; } } } else { nseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[0], 16); if (nseg == NULL) { sa->sge->intr_cnt.tx_no_dma2++; /* * ran out of space. Gona bale */ rv = 0; goto error1; } } #else /* defined(__sparc) */ nseg = ch_bind_dma_handle(sa, len, (void *)mp->b_rptr, &hmp[0], 16); if (nseg == NULL) { sa->sge->intr_cnt.tx_no_dma2++; /* * ran out of space. Gona bale */ rv = 0; goto error1; } #endif /* defined(__sparc) */ /* * dummy arp message to handle PR3309 & PR2928 */ if (flg & CH_ARP) hmp->ce_flg |= DH_ARP; } if (sge_data_out(sa->sge, 0, m0, hmp, nseg, flg) == 0) { if (cm_flg) kmem_free(hmp, mseg * sizeof (cmdQ_ce_t)); return (0); } /* * set a flag so we'll restart upper layer when * resources become available. */ if ((flg & CH_ARP) == 0) sa->ch_blked = 1; rv = 1; error: /* * unmap the physical addresses allocated earlier. */ cmp = hmp; for (--nseg; nseg >= 0; nseg--) { if (cmp->ce_dh) { if (cmp->ce_flg == DH_DMA) ch_unbind_dma_handle(sa, cmp->ce_dh); #if defined(__sparc) else ch_unbind_dvma_handle(sa, cmp->ce_dh); #endif } cmp++; } error1: /* free the temporary array */ if (cm_flg) kmem_free(hmp, mseg * sizeof (cmdQ_ce_t)); /* * if we've allocated an mblk above, then we need to free it * before returning. This is safe since we haven't done anything to * the original message. The caller, gld, will still have a pointer * to the original mblk. */ if (rv == 1) { if (freeme) { /* we had to allocate an mblk. Free it. */ freeb(m0); } else { /* adjust the mblk back to original start */ if (flg & CH_NO_CPL) m0->b_rptr += SZ_CPL_TX_PKT; } } else { freemsg(m0); sa->oerr++; } return (rv); } /* KLUDGE ALERT. HARD WIRED TO PORT ZERO */ void pe_set_mac(ch_t *sa, unsigned char *ac_enaddr) { sa->port[0].mac->ops->macaddress_set(sa->port[0].mac, ac_enaddr); } /* KLUDGE ALERT. HARD WIRED TO PORT ZERO */ unsigned char * pe_get_mac(ch_t *sa) { return (sa->port[0].enaddr); } /* KLUDGE ALERT. HARD WIRED TO ONE PORT */ void pe_set_promiscuous(ch_t *sa, int flag) { struct cmac *mac = sa->port[0].mac; struct t1_rx_mode rm; switch (flag) { case 0: /* turn off promiscuous mode */ sa->ch_flags &= ~(PEPROMISC|PEALLMULTI); break; case 1: /* turn on promiscuous mode */ sa->ch_flags |= PEPROMISC; break; case 2: /* turn on multicast reception */ sa->ch_flags |= PEALLMULTI; break; } mutex_enter(&sa->ch_mc_lck); rm.chp = sa; rm.mc = sa->ch_mc; mac->ops->set_rx_mode(mac, &rm); mutex_exit(&sa->ch_mc_lck); } int pe_set_mc(ch_t *sa, uint8_t *ep, int flg) { struct cmac *mac = sa->port[0].mac; struct t1_rx_mode rm; if (flg == GLD_MULTI_ENABLE) { ch_mc_t *mcp; mcp = (ch_mc_t *)kmem_zalloc(sizeof (struct ch_mc), KM_NOSLEEP); if (mcp == NULL) return (GLD_NORESOURCES); bcopy(ep, &mcp->cmc_mca, 6); mutex_enter(&sa->ch_mc_lck); mcp->cmc_next = sa->ch_mc; sa->ch_mc = mcp; sa->ch_mc_cnt++; mutex_exit(&sa->ch_mc_lck); } else if (flg == GLD_MULTI_DISABLE) { ch_mc_t **p = &sa->ch_mc; ch_mc_t *q = NULL; mutex_enter(&sa->ch_mc_lck); p = &sa->ch_mc; while (*p) { if (bcmp(ep, (*p)->cmc_mca, 6) == 0) { q = *p; *p = (*p)->cmc_next; kmem_free(q, sizeof (*q)); sa->ch_mc_cnt--; break; } p = &(*p)->cmc_next; } mutex_exit(&sa->ch_mc_lck); if (q == NULL) return (GLD_BADARG); } else return (GLD_BADARG); mutex_enter(&sa->ch_mc_lck); rm.chp = sa; rm.mc = sa->ch_mc; mac->ops->set_rx_mode(mac, &rm); mutex_exit(&sa->ch_mc_lck); return (GLD_SUCCESS); } /* * return: speed - bandwidth of interface * return: intrcnt - # interrupts * return: norcvbuf - # recedived packets dropped by driver * return: oerrors - # bad send packets * return: ierrors - # bad receive packets * return: underrun - # bad underrun xmit packets * return: overrun - # bad overrun recv packets * return: framing - # bad aligned recv packets * return: crc - # bad FCS (crc) recv packets * return: carrier - times carrier was lost * return: collisions - # xmit collisions * return: xcollisions - # xmit pkts dropped due to collisions * return: late - # late xmit collisions * return: defer - # deferred xmit packets * return: xerrs - # xmit dropped packets * return: rerrs - # recv dropped packets * return: toolong - # recv pkts too long * return: runt - # recv runt pkts * return: multixmt - # multicast pkts xmitted * return: multircv - # multicast pkts recved * return: brdcstxmt - # broadcast pkts xmitted * return: brdcstrcv - # broadcast pkts rcv */ int pe_get_stats(ch_t *sa, 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) { struct pe_port_t *pt; int line_speed; int line_duplex; int line_is_active; uint64_t v; const struct cmac_statistics *sp; pt = &(sa->port[0]); (void) pt->phy->ops->get_link_status(pt->phy, &line_is_active, &line_speed, &line_duplex, NULL); switch (line_speed) { case SPEED_10: *speed = 10000000; break; case SPEED_100: *speed = 100000000; break; case SPEED_1000: *speed = 1000000000; break; case SPEED_10000: /* * kludge to get 10,000,000,000 constant (and keep * compiler happy). */ v = 10000000; v *= 1000; *speed = v; break; default: goto error; } *intrcnt = sa->isr_intr; *norcvbuf = sa->norcvbuf; sp = sa->port[0].mac->ops->statistics_update(sa->port[0].mac, MAC_STATS_UPDATE_FULL); *ierrors = sp->RxOctetsBad; /* * not sure this is correct. # aborted at driver level + * # at hardware level */ *oerrors = sa->oerr + sp->TxFramesAbortedDueToXSCollisions + sp->TxUnderrun + sp->TxLengthErrors + sp->TxInternalMACXmitError + sp->TxFramesWithExcessiveDeferral + sp->TxFCSErrors; *underrun = sp->TxUnderrun; *overrun = sp->RxFrameTooLongErrors; *framing = sp->RxAlignErrors; *crc = sp->RxFCSErrors; *carrier = 0; /* need to find this */ *collisions = sp->TxTotalCollisions; *xcollisions = sp->TxFramesAbortedDueToXSCollisions; *late = sp->TxLateCollisions; *defer = sp->TxFramesWithDeferredXmissions; *xerrs = sp->TxUnderrun + sp->TxLengthErrors + sp->TxInternalMACXmitError + sp->TxFCSErrors; *rerrs = sp->RxSymbolErrors + sp->RxSequenceErrors + sp->RxRuntErrors + sp->RxJabberErrors + sp->RxInternalMACRcvError + sp->RxInRangeLengthErrors + sp->RxOutOfRangeLengthField; *toolong = sp->RxFrameTooLongErrors; *runt = sp->RxRuntErrors; *multixmt = sp->TxMulticastFramesOK; *multircv = sp->RxMulticastFramesOK; *brdcstxmt = sp->TxBroadcastFramesOK; *brdcstrcv = sp->RxBroadcastFramesOK; return (0); error: *speed = 0; *intrcnt = 0; *norcvbuf = 0; *norcvbuf = 0; *oerrors = 0; *ierrors = 0; *underrun = 0; *overrun = 0; *framing = 0; *crc = 0; *carrier = 0; *collisions = 0; *xcollisions = 0; *late = 0; *defer = 0; *xerrs = 0; *rerrs = 0; *toolong = 0; *runt = 0; *multixmt = 0; *multircv = 0; *brdcstxmt = 0; *brdcstrcv = 0; return (1); } uint32_t ch_gtm = 0; /* Default: Global Tunnel Mode off */ uint32_t ch_global_config = 0x07000000; /* Default: errors, warnings, status */ uint32_t ch_is_asic = 0; /* Default: non-ASIC */ uint32_t ch_link_speed = PE_LINK_SPEED_AUTONEG; /* Default: auto-negoiate */ uint32_t ch_num_of_ports = 1; /* Default: 1 port */ uint32_t ch_tp_reset_cm = 1; /* Default: reset CM memory map */ uint32_t ch_phy_tx_fifo = 0; /* Default: 0 phy tx fifo depth */ uint32_t ch_phy_rx_fifo = 0; /* Default: 0 phy rx fifo depth */ uint32_t ch_phy_force_master = 1; /* Default: link always master mode */ uint32_t ch_mc5_rtbl_size = 2048; /* Default: TCAM routing table size */ uint32_t ch_mc5_dbsvr_size = 128; /* Default: TCAM server size */ uint32_t ch_mc5_parity = 1; /* Default: parity error checking */ uint32_t ch_mc5_issue_syn = 0; /* Default: Allow transaction overlap */ uint32_t ch_packet_tracing = 0; /* Default: no packet tracing */ uint32_t ch_server_region_len = DEFAULT_SERVER_REGION_LEN; uint32_t ch_rt_region_len = DEFAULT_RT_REGION_LEN; uint32_t ch_offload_ip_cksum = 0; /* Default: no checksum offloading */ uint32_t ch_offload_udp_cksum = 1; /* Default: offload UDP ckecksum */ uint32_t ch_offload_tcp_cksum = 1; /* Default: offload TCP checksum */ uint32_t ch_sge_cmdq_threshold = 0; /* Default: threshold 0 */ uint32_t ch_sge_flq_threshold = 0; /* Default: SGE flq threshold */ uint32_t ch_sge_cmdq0_cnt = /* Default: cmd queue 0 size */ SGE_CMDQ0_CNT; uint32_t ch_sge_cmdq1_cnt = /* Default: cmd queue 1 size */ SGE_CMDQ0_CNT; uint32_t ch_sge_flq0_cnt = /* Default: free list queue-0 length */ SGE_FLQ0_CNT; uint32_t ch_sge_flq1_cnt = /* Default: free list queue-1 length */ SGE_FLQ0_CNT; uint32_t ch_sge_respq_cnt = /* Default: reqsponse queue size */ SGE_RESPQ_CNT; uint32_t ch_stats = 1; /* Default: Automatic Update MAC stats */ uint32_t ch_tx_delay_us = 0; /* Default: No Msec delay to Tx pkts */ int32_t ch_chip = -1; /* Default: use hardware lookup tbl */ uint32_t ch_exit_early = 0; /* Default: complete initialization */ uint32_t ch_rb_num_of_entries = 1000; /* Default: number ring buffer entries */ uint32_t ch_rb_size_of_entries = 64; /* Default: ring buffer entry size */ uint32_t ch_rb_flag = 1; /* Default: ring buffer flag */ uint32_t ch_type; uint64_t ch_cat_opt0 = 0; uint64_t ch_cat_opt1 = 0; uint32_t ch_timer_delay = 0; /* Default: use value from board entry */ int pe_attach(ch_t *chp) { int return_val = 1; const struct board_info *bi; uint32_t pcix_cmd; (void) ch_set_config_data(chp); bi = pe_sa_init(chp); if (bi == 0) return (1); if (t1_init_sw_modules(chp, bi) < 0) return (1); if (pe_small_rbuf_pool_init(chp) == NULL) return (1); if (pe_big_rbuf_pool_init(chp) == NULL) return (1); /* * We gain significaint performance improvements when we * increase the PCI's maximum memory read byte count to * 2K(HW doesn't support 4K at this time) and set the PCI's * maximum outstanding split transactions to 4. We want to do * this for 10G. Done by software utility. */ if (board_info(chp)->caps & SUPPORTED_10000baseT_Full) { (void) t1_os_pci_read_config_4(chp, A_PCICFG_PCIX_CMD, &pcix_cmd); /* * if the burstsize is set, then use it instead of default */ if (chp->ch_config.burstsize_set) { pcix_cmd &= ~0xc0000; pcix_cmd |= (chp->ch_config.burstsize << 18); } /* * if the split transaction count is set, then use it. */ if (chp->ch_config.transaction_cnt_set) { pcix_cmd &= ~ 0x700000; pcix_cmd |= (chp->ch_config.transaction_cnt << 20); } /* * set ralaxed ordering flag as configured in chxge.conf */ pcix_cmd |= (chp->ch_config.relaxed_ordering << 17); (void) t1_os_pci_write_config_4(chp, A_PCICFG_PCIX_CMD, pcix_cmd); } /* * set the latency time to F8 for 10G cards. * Done by software utiltiy. */ if (enable_latency_timer) { if (board_info(chp)->caps & SUPPORTED_10000baseT_Full) { (void) t1_os_pci_write_config_4(chp, 0xc, 0xf800); } } /* * update mtu table (regs: 0x404 - 0x420) with bigger values than * default. */ update_mtu_tab(chp); /* * Clear all interrupts now. Don't enable * them until later. */ t1_interrupts_clear(chp); /* * Function succeeded. */ return_val = 0; return (return_val); } /* * DESC: Read variables set in /boot/loader.conf and save * them internally. These internal values are then * used to make decisions at run-time on behavior thus * allowing a certain level of customization. * OUT: p_config - pointer to config structure that * contains all of the new values. * RTN: 0 - Success; */ static int ch_set_config_data(ch_t *chp) { pe_config_data_t *p_config = (pe_config_data_t *)&chp->config_data; bzero(p_config, sizeof (pe_config_data_t)); /* * Global Tunnel Mode configuration */ p_config->gtm = ch_gtm; p_config->global_config = ch_global_config; if (p_config->gtm) p_config->global_config |= CFGMD_TUNNEL; p_config->tp_reset_cm = ch_tp_reset_cm; p_config->is_asic = ch_is_asic; /* * MC5 configuration. */ p_config->mc5_rtbl_size = ch_mc5_rtbl_size; p_config->mc5_dbsvr_size = ch_mc5_dbsvr_size; p_config->mc5_parity = ch_mc5_parity; p_config->mc5_issue_syn = ch_mc5_issue_syn; p_config->offload_ip_cksum = ch_offload_ip_cksum; p_config->offload_udp_cksum = ch_offload_udp_cksum; p_config->offload_tcp_cksum = ch_offload_tcp_cksum; p_config->packet_tracing = ch_packet_tracing; p_config->server_region_len = ch_server_region_len; p_config->rt_region_len = ch_rt_region_len; /* * Link configuration. * * 5-auto-neg 2-1000Gbps; 1-100Gbps; 0-10Gbps */ p_config->link_speed = ch_link_speed; p_config->num_of_ports = ch_num_of_ports; /* * Catp options */ p_config->cat_opt0 = ch_cat_opt0; p_config->cat_opt1 = ch_cat_opt1; /* * SGE configuration. */ p_config->sge_cmdq0_cnt = ch_sge_cmdq0_cnt; p_config->sge_cmdq1_cnt = ch_sge_cmdq1_cnt; p_config->sge_flq0_cnt = ch_sge_flq0_cnt; p_config->sge_flq1_cnt = ch_sge_flq1_cnt; p_config->sge_respq_cnt = ch_sge_respq_cnt; p_config->phy_rx_fifo = ch_phy_rx_fifo; p_config->phy_tx_fifo = ch_phy_tx_fifo; p_config->sge_cmdq_threshold = ch_sge_cmdq_threshold; p_config->sge_flq_threshold = ch_sge_flq_threshold; p_config->phy_force_master = ch_phy_force_master; p_config->rb_num_of_entries = ch_rb_num_of_entries; p_config->rb_size_of_entries = ch_rb_size_of_entries; p_config->rb_flag = ch_rb_flag; p_config->exit_early = ch_exit_early; p_config->chip = ch_chip; p_config->stats = ch_stats; p_config->tx_delay_us = ch_tx_delay_us; return (0); } static const struct board_info * pe_sa_init(ch_t *sa) { uint16_t device_id; uint16_t device_subid; const struct board_info *bi; sa->config = sa->config_data.global_config; device_id = pci_config_get16(sa->ch_hpci, 2); device_subid = pci_config_get16(sa->ch_hpci, 0x2e); bi = t1_get_board_info_from_ids(device_id, device_subid); if (bi == NULL) { cmn_err(CE_NOTE, "The adapter with device_id %d %d is not supported.\n", device_id, device_subid); return (NULL); } if (t1_get_board_rev(sa, bi, &sa->params)) { cmn_err(CE_NOTE, "unknown device_id %d %d\n", device_id, device_subid); return ((const struct board_info *)NULL); } return (bi); } /* * allocate pool of small receive buffers (with vaddr & paddr) and * receiver buffer control structure (ch_esb_t *rbp). * XXX we should allow better tuning of the # of preallocated * free buffers against the # of freelist entries. */ static int pe_small_rbuf_pool_init(ch_t *sa) { int i; ch_esb_t *rbp; extern uint32_t sge_flq0_cnt; extern uint32_t sge_flq1_cnt; int size; uint32_t j; if (is_T2(sa)) size = sge_flq1_cnt * fl_sz_multiplier; else size = sge_flq0_cnt * fl_sz_multiplier; mutex_init(&sa->ch_small_esbl, NULL, MUTEX_DRIVER, sa->ch_icookp); mutex_enter(&in_use_l); j = in_use_index++; if (in_use_index >= SZ_INUSE) in_use_index = 0; mutex_exit(&in_use_l); sa->ch_small_owner = NULL; sa->ch_sm_index = j; sa->ch_small_esb_free = NULL; for (i = 0; i < size; i++) { rbp = ch_alloc_small_esbbuf(sa, j); if (rbp == NULL) goto error; /* * add entry to free list */ rbp->cs_next = sa->ch_small_esb_free; sa->ch_small_esb_free = rbp; /* * add entry to owned list */ rbp->cs_owner = sa->ch_small_owner; sa->ch_small_owner = rbp; } return (1); error: sa->ch_small_owner = NULL; /* free whatever we've already allocated */ pe_rbuf_pool_free(sa); return (0); } /* * allocate pool of receive buffers (with vaddr & paddr) and * receiver buffer control structure (ch_esb_t *rbp). * XXX we should allow better tuning of the # of preallocated * free buffers against the # of freelist entries. */ static int pe_big_rbuf_pool_init(ch_t *sa) { int i; ch_esb_t *rbp; extern uint32_t sge_flq0_cnt; extern uint32_t sge_flq1_cnt; int size; uint32_t j; if (is_T2(sa)) size = sge_flq0_cnt * fl_sz_multiplier; else size = sge_flq1_cnt * fl_sz_multiplier; mutex_init(&sa->ch_big_esbl, NULL, MUTEX_DRIVER, sa->ch_icookp); mutex_enter(&in_use_l); j = in_use_index++; if (in_use_index >= SZ_INUSE) in_use_index = 0; mutex_exit(&in_use_l); sa->ch_big_owner = NULL; sa->ch_big_index = j; sa->ch_big_esb_free = NULL; for (i = 0; i < size; i++) { rbp = ch_alloc_big_esbbuf(sa, j); if (rbp == NULL) goto error; rbp->cs_next = sa->ch_big_esb_free; sa->ch_big_esb_free = rbp; /* * add entry to owned list */ rbp->cs_owner = sa->ch_big_owner; sa->ch_big_owner = rbp; } return (1); error: sa->ch_big_owner = NULL; /* free whatever we've already allocated */ pe_rbuf_pool_free(sa); return (0); } /* * allocate receive buffer structure and dma mapped buffer (SGE_SM_BUF_SZ bytes) * note that we will DMA at a 2 byte offset for Solaris when checksum offload * is enabled. */ static ch_esb_t * ch_alloc_small_esbbuf(ch_t *sa, uint32_t i) { ch_esb_t *rbp; rbp = (ch_esb_t *)kmem_zalloc(sizeof (ch_esb_t), KM_SLEEP); if (rbp == NULL) { return ((ch_esb_t *)0); } #if BYTE_ORDER == BIG_ENDIAN rbp->cs_buf = (caddr_t)ch_alloc_dma_mem(sa, 1, DMA_STREAM|DMA_SMALN, SGE_SM_BUF_SZ(sa), &rbp->cs_pa, &rbp->cs_dh, &rbp->cs_ah); #else rbp->cs_buf = (caddr_t)ch_alloc_dma_mem(sa, 0, DMA_STREAM|DMA_SMALN, SGE_SM_BUF_SZ(sa), &rbp->cs_pa, &rbp->cs_dh, &rbp->cs_ah); #endif if (rbp->cs_buf == NULL) { kmem_free(rbp, sizeof (ch_esb_t)); return ((ch_esb_t *)0); } rbp->cs_sa = sa; rbp->cs_index = i; rbp->cs_frtn.free_func = (void (*)())&ch_small_rbuf_recycle; rbp->cs_frtn.free_arg = (caddr_t)rbp; return (rbp); } /* * allocate receive buffer structure and dma mapped buffer (SGE_BG_BUF_SZ bytes) * note that we will DMA at a 2 byte offset for Solaris when checksum offload * is enabled. */ static ch_esb_t * ch_alloc_big_esbbuf(ch_t *sa, uint32_t i) { ch_esb_t *rbp; rbp = (ch_esb_t *)kmem_zalloc(sizeof (ch_esb_t), KM_SLEEP); if (rbp == NULL) { return ((ch_esb_t *)0); } #if BYTE_ORDER == BIG_ENDIAN rbp->cs_buf = (caddr_t)ch_alloc_dma_mem(sa, 1, DMA_STREAM|DMA_BGALN, SGE_BG_BUF_SZ(sa), &rbp->cs_pa, &rbp->cs_dh, &rbp->cs_ah); #else rbp->cs_buf = (caddr_t)ch_alloc_dma_mem(sa, 0, DMA_STREAM|DMA_BGALN, SGE_BG_BUF_SZ(sa), &rbp->cs_pa, &rbp->cs_dh, &rbp->cs_ah); #endif if (rbp->cs_buf == NULL) { kmem_free(rbp, sizeof (ch_esb_t)); return ((ch_esb_t *)0); } rbp->cs_sa = sa; rbp->cs_index = i; rbp->cs_frtn.free_func = (void (*)())&ch_big_rbuf_recycle; rbp->cs_frtn.free_arg = (caddr_t)rbp; return (rbp); } /* * free entries on the receive buffer list. */ void pe_rbuf_pool_free(ch_t *sa) { ch_esb_t *rbp; mutex_enter(&sa->ch_small_esbl); /* * Now set-up the rest to commit suicide. */ while (sa->ch_small_owner) { rbp = sa->ch_small_owner; sa->ch_small_owner = rbp->cs_owner; rbp->cs_owner = NULL; rbp->cs_flag = 1; } while ((rbp = sa->ch_small_esb_free) != NULL) { /* advance head ptr to next entry */ sa->ch_small_esb_free = rbp->cs_next; /* free private buffer allocated in ch_alloc_esbbuf() */ ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah); /* free descripter buffer */ kmem_free(rbp, sizeof (ch_esb_t)); } mutex_exit(&sa->ch_small_esbl); /* destroy ch_esbl lock */ mutex_destroy(&sa->ch_small_esbl); mutex_enter(&sa->ch_big_esbl); /* * Now set-up the rest to commit suicide. */ while (sa->ch_big_owner) { rbp = sa->ch_big_owner; sa->ch_big_owner = rbp->cs_owner; rbp->cs_owner = NULL; rbp->cs_flag = 1; } while ((rbp = sa->ch_big_esb_free) != NULL) { /* advance head ptr to next entry */ sa->ch_big_esb_free = rbp->cs_next; /* free private buffer allocated in ch_alloc_esbbuf() */ ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah); /* free descripter buffer */ kmem_free(rbp, sizeof (ch_esb_t)); } mutex_exit(&sa->ch_big_esbl); /* destroy ch_esbl lock */ mutex_destroy(&sa->ch_big_esbl); } void ch_small_rbuf_recycle(ch_esb_t *rbp) { ch_t *sa = rbp->cs_sa; if (rbp->cs_flag) { uint32_t i; /* * free private buffer allocated in ch_alloc_esbbuf() */ ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah); i = rbp->cs_index; /* * free descripter buffer */ kmem_free(rbp, sizeof (ch_esb_t)); /* * decrement count of receive buffers freed by callback * We decrement here so anyone trying to do fini will * only remove the driver once the counts go to 0. */ atomic_dec_32(&buffers_in_use[i]); return; } mutex_enter(&sa->ch_small_esbl); rbp->cs_next = sa->ch_small_esb_free; sa->ch_small_esb_free = rbp; mutex_exit(&sa->ch_small_esbl); /* * decrement count of receive buffers freed by callback */ atomic_dec_32(&buffers_in_use[rbp->cs_index]); } /* * callback function from freeb() when esballoced mblk freed. */ void ch_big_rbuf_recycle(ch_esb_t *rbp) { ch_t *sa = rbp->cs_sa; if (rbp->cs_flag) { uint32_t i; /* * free private buffer allocated in ch_alloc_esbbuf() */ ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah); i = rbp->cs_index; /* * free descripter buffer */ kmem_free(rbp, sizeof (ch_esb_t)); /* * decrement count of receive buffers freed by callback * We decrement here so anyone trying to do fini will * only remove the driver once the counts go to 0. */ atomic_dec_32(&buffers_in_use[i]); return; } mutex_enter(&sa->ch_big_esbl); rbp->cs_next = sa->ch_big_esb_free; sa->ch_big_esb_free = rbp; mutex_exit(&sa->ch_big_esbl); /* * decrement count of receive buffers freed by callback */ atomic_dec_32(&buffers_in_use[rbp->cs_index]); } /* * get a pre-allocated, pre-mapped receive buffer from free list. * (used sge.c) */ ch_esb_t * ch_get_small_rbuf(ch_t *sa) { ch_esb_t *rbp; mutex_enter(&sa->ch_small_esbl); rbp = sa->ch_small_esb_free; if (rbp) { sa->ch_small_esb_free = rbp->cs_next; } mutex_exit(&sa->ch_small_esbl); return (rbp); } /* * get a pre-allocated, pre-mapped receive buffer from free list. * (used sge.c) */ ch_esb_t * ch_get_big_rbuf(ch_t *sa) { ch_esb_t *rbp; mutex_enter(&sa->ch_big_esbl); rbp = sa->ch_big_esb_free; if (rbp) { sa->ch_big_esb_free = rbp->cs_next; } mutex_exit(&sa->ch_big_esbl); return (rbp); } void pe_detach(ch_t *sa) { (void) sge_stop(sa->sge); pe_free_driver_resources(sa); } static void pe_free_driver_resources(ch_t *sa) { if (sa) { t1_free_sw_modules(sa); /* free pool of receive buffers */ pe_rbuf_pool_free(sa); } } /* * Processes elmer0 external interrupts in process context. */ static void ext_intr_task(ch_t *adapter) { u32 enable; (void) elmer0_ext_intr_handler(adapter); /* Now reenable external interrupts */ t1_write_reg_4(adapter, A_PL_CAUSE, F_PL_INTR_EXT); enable = t1_read_reg_4(adapter, A_PL_ENABLE); t1_write_reg_4(adapter, A_PL_ENABLE, enable | F_PL_INTR_EXT); adapter->slow_intr_mask |= F_PL_INTR_EXT; } /* * Interrupt-context handler for elmer0 external interrupts. */ void t1_os_elmer0_ext_intr(ch_t *adapter) { u32 enable = t1_read_reg_4(adapter, A_PL_ENABLE); adapter->slow_intr_mask &= ~F_PL_INTR_EXT; t1_write_reg_4(adapter, A_PL_ENABLE, enable & ~F_PL_INTR_EXT); #ifdef NOTYET schedule_work(&adapter->ext_intr_handler_task); #else ext_intr_task(adapter); #endif } uint8_t * t1_get_next_mcaddr(struct t1_rx_mode *rmp) { uint8_t *addr = 0; if (rmp->mc) { addr = rmp->mc->cmc_mca; rmp->mc = rmp->mc->cmc_next; } return (addr); } void pe_dma_handle_init(ch_t *chp, int cnt) { free_dh_t *dhe; #if defined(__sparc) int tcnt = cnt/2; for (; cnt; cnt--) { dhe = ch_get_dvma_handle(chp); if (dhe == NULL) break; mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_vdh; chp->ch_vdh = dhe; mutex_exit(&chp->ch_dh_lck); } cnt += tcnt; #endif while (cnt--) { dhe = ch_get_dma_handle(chp); if (dhe == NULL) return; mutex_enter(&chp->ch_dh_lck); dhe->dhe_next = chp->ch_dh; chp->ch_dh = dhe; mutex_exit(&chp->ch_dh_lck); } } /* * Write new values to the MTU table. Caller must validate that the new MTUs * are in ascending order. params.mtus[] is initialized by init_mtus() * called in t1_init_sw_modules(). */ #define MTUREG(idx) (A_TP_MTU_REG0 + (idx) * 4) static void update_mtu_tab(ch_t *adapter) { int i; for (i = 0; i < NMTUS; ++i) { int mtu = (unsigned int)adapter->params.mtus[i]; t1_write_reg_4(adapter, MTUREG(i), mtu); } } static int pe_change_mtu(ch_t *chp) { struct cmac *mac = chp->port[0].mac; int ret; if (!mac->ops->set_mtu) { return (EOPNOTSUPP); } if (chp->ch_mtu < 68) { return (EINVAL); } if (ret = mac->ops->set_mtu(mac, chp->ch_mtu)) { return (ret); } return (0); } typedef struct fake_arp { char fa_dst[6]; /* ethernet header */ char fa_src[6]; /* ethernet header */ ushort_t fa_typ; /* ethernet header */ ushort_t fa_hrd; /* arp */ ushort_t fa_pro; char fa_hln; char fa_pln; ushort_t fa_op; char fa_src_mac[6]; uint_t fa_src_ip; char fa_dst_mac[6]; char fa_dst_ip[4]; } fake_arp_t; /* * PR2928 & PR3309 * construct packet in mblk and attach it to sge structure. */ static int pe_make_fake_arp(ch_t *chp, unsigned char *arpp) { pesge *sge = chp->sge; mblk_t *bp; fake_arp_t *fap; static char buf[6] = {0, 7, 0x43, 0, 0, 0}; struct cpl_tx_pkt *cpl; bp = allocb(sizeof (struct fake_arp) + SZ_CPL_TX_PKT, BPRI_HI); if (bp == NULL) { return (1); } bzero(bp->b_rptr, sizeof (struct fake_arp) + SZ_CPL_TX_PKT); /* fill in cpl header */ cpl = (struct cpl_tx_pkt *)bp->b_rptr; cpl->opcode = CPL_TX_PKT; cpl->iff = 0; /* XXX port 0 needs fixing with NEMO */ cpl->ip_csum_dis = 1; /* no IP header cksum */ cpl->l4_csum_dis = 1; /* no tcp/udp cksum */ cpl->vlan_valid = 0; /* no vlan */ fap = (fake_arp_t *)&bp->b_rptr[SZ_CPL_TX_PKT]; bcopy(arpp, fap, sizeof (*fap)); /* copy first arp to mblk */ bcopy(buf, fap->fa_dst, 6); /* overwrite dst mac */ chp->ch_ip = fap->fa_src_ip; /* not used yet */ bcopy(buf, fap->fa_dst_mac, 6); /* overwrite dst mac */ bp->b_wptr = bp->b_rptr + sizeof (struct fake_arp)+SZ_CPL_TX_PKT; sge_add_fake_arp(sge, (void *)bp); return (0); } /* * PR2928 & PR3309 * free the fake arp's mblk on sge structure. */ void pe_free_fake_arp(void *arp) { mblk_t *bp = (mblk_t *)(arp); freemsg(bp); } /* * extract ip address of nic from first outgoing arp. */ static uint32_t pe_get_ip(unsigned char *arpp) { fake_arp_t fap; /* * first copy packet to buffer so we know * it will be properly aligned. */ bcopy(arpp, &fap, sizeof (fap)); /* copy first arp to buffer */ return (fap.fa_src_ip); } /* ARGSUSED */ void t1_os_link_changed(ch_t *obj, int port_id, int link_status, int speed, int duplex, int fc) { gld_mac_info_t *macinfo = obj->ch_macp; if (link_status) { gld_linkstate(macinfo, GLD_LINKSTATE_UP); /* * Link states should be reported to user * whenever it changes */ cmn_err(CE_NOTE, "%s: link is up", adapter_name(obj)); } else { gld_linkstate(macinfo, GLD_LINKSTATE_DOWN); /* * Link states should be reported to user * whenever it changes */ cmn_err(CE_NOTE, "%s: link is down", adapter_name(obj)); } }