#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);
#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);
int enable_latency_timer = 1;
int enable_checksum_offload = 1;
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);
}
void
pe_init(void* xsa)
{
ch_t *sa = NULL;
int i = 0;
sa = (ch_t *)xsa;
if (sa->init_counter == 0) {
for_each_port(sa, i) {
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);
if (sa->ch_config.cksum_enabled) {
if (sa->config_data.offload_ip_cksum) {
t1_tp_set_ip_checksum_offload(sa->tp, 1);
}
if (sa->config_data.offload_tcp_cksum) {
t1_tp_set_tcp_checksum_offload(sa->tp, 1);
}
if (sa->config_data.offload_udp_cksum) {
t1_tp_set_udp_checksum_offload(sa->tp, 1);
}
}
sa->ch_flags |= PEINITDONE;
sa->init_counter++;
}
(void) sge_start(sa->sge);
t1_interrupts_enable(sa);
(void) pe_change_mtu(sa);
#ifdef HOST_PAUSE
(void) t1_tpi_read(sa, SUNI1x10GEXP_REG_TXXG_CONFIG_1 << 2,
&sa->txxg_cfg1);
#endif
}
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);
}
void
pe_stop(ch_t *sa)
{
t1_interrupts_disable(sa);
(void) sge_stop(sa->sge);
mutex_enter(&sa->ch_intr);
mutex_exit(&sa->ch_intr);
}
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];
int cm_flg = 0;
int nseg = 0;
int mseg = 16;
int freeme = 0;
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;
#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;
(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);
}
sa->ch_blked = 1;
return (1);
}
#endif
if (flg & CH_NO_CPL) {
struct cpl_tx_pkt *cpl;
if (sa->ch_ip == 0) {
ushort_t ethertype = ntohs(*(short *)&mp->b_rptr[12]);
if (ethertype == ETHERTYPE_ARP) {
if (is_T2(sa)) {
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 ((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;
}
cpl = (struct cpl_tx_pkt *)mp->b_rptr;
cpl->opcode = CPL_TX_PKT;
cpl->iff = 0;
cpl->ip_csum_dis = 1;
cpl->l4_csum_dis =
flg & CH_NO_HWCKSUM;
cpl->vlan_valid = 0;
}
if (m0->b_cont) {
#ifdef SUN_KSTATS
sa->sge->intr_cnt.tx_multi_mblks++;
#endif
while (mp) {
int lseg;
int len;
len = MLEN(mp);
if (len == 0) {
mp = mp->b_cont;
continue;
}
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;
}
#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 == 0) {
sa->sge->intr_cnt.tx_no_dvma1++;
if ((lseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr,
&hmp[nseg],
mseg - nseg)) == 0) {
sa->sge->intr_cnt.tx_no_dma1++;
rv = 0;
if (nseg)
goto error;
goto error1;
}
}
} else {
lseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr, &hmp[nseg],
mseg - nseg);
if (lseg == 0) {
sa->sge->intr_cnt.tx_no_dma1++;
rv = 0;
if (nseg)
goto error;
goto error1;
}
}
#else
lseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr, &hmp[nseg],
mseg - nseg);
if (lseg == 0) {
sa->sge->intr_cnt.tx_no_dma1++;
rv = 0;
if (nseg)
goto error;
goto error1;
}
#endif
nseg += lseg;
mp = mp->b_cont;
}
if ((nseg == 0) || (freeme && (nseg == 1))) {
rv = 0;
goto error1;
}
} else {
int len;
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 == 0) {
sa->sge->intr_cnt.tx_no_dvma2++;
nseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr,
&hmp[0], 16);
if (nseg == 0) {
sa->sge->intr_cnt.tx_no_dma2++;
rv = 0;
goto error1;
}
}
} else {
nseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr, &hmp[0], 16);
if (nseg == 0) {
sa->sge->intr_cnt.tx_no_dma2++;
rv = 0;
goto error1;
}
}
#else
nseg = ch_bind_dma_handle(sa, len,
(void *)mp->b_rptr, &hmp[0], 16);
if (nseg == 0) {
sa->sge->intr_cnt.tx_no_dma2++;
rv = 0;
goto error1;
}
#endif
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);
}
if ((flg & CH_ARP) == 0)
sa->ch_blked = 1;
rv = 1;
error:
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:
if (cm_flg)
kmem_free(hmp, mseg * sizeof (cmdQ_ce_t));
if (rv == 1) {
if (freeme) {
freeb(m0);
} else {
if (flg & CH_NO_CPL)
m0->b_rptr += SZ_CPL_TX_PKT;
}
} else {
freemsg(m0);
sa->oerr++;
}
return (rv);
}
void
pe_set_mac(ch_t *sa, unsigned char *ac_enaddr)
{
sa->port[0].mac->ops->macaddress_set(sa->port[0].mac, ac_enaddr);
}
unsigned char *
pe_get_mac(ch_t *sa)
{
return (sa->port[0].enaddr);
}
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:
sa->ch_flags &= ~(PEPROMISC|PEALLMULTI);
break;
case 1:
sa->ch_flags |= PEPROMISC;
break;
case 2:
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);
}
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:
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;
*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;
*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;
uint32_t ch_global_config = 0x07000000;
uint32_t ch_is_asic = 0;
uint32_t ch_link_speed = PE_LINK_SPEED_AUTONEG;
uint32_t ch_num_of_ports = 1;
uint32_t ch_tp_reset_cm = 1;
uint32_t ch_phy_tx_fifo = 0;
uint32_t ch_phy_rx_fifo = 0;
uint32_t ch_phy_force_master = 1;
uint32_t ch_mc5_rtbl_size = 2048;
uint32_t ch_mc5_dbsvr_size = 128;
uint32_t ch_mc5_parity = 1;
uint32_t ch_mc5_issue_syn = 0;
uint32_t ch_packet_tracing = 0;
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;
uint32_t ch_offload_udp_cksum = 1;
uint32_t ch_offload_tcp_cksum = 1;
uint32_t ch_sge_cmdq_threshold = 0;
uint32_t ch_sge_flq_threshold = 0;
uint32_t ch_sge_cmdq0_cnt =
SGE_CMDQ0_CNT;
uint32_t ch_sge_cmdq1_cnt =
SGE_CMDQ0_CNT;
uint32_t ch_sge_flq0_cnt =
SGE_FLQ0_CNT;
uint32_t ch_sge_flq1_cnt =
SGE_FLQ0_CNT;
uint32_t ch_sge_respq_cnt =
SGE_RESPQ_CNT;
uint32_t ch_stats = 1;
uint32_t ch_tx_delay_us = 0;
int32_t ch_chip = -1;
uint32_t ch_exit_early = 0;
uint32_t ch_rb_num_of_entries = 1000;
uint32_t ch_rb_size_of_entries = 64;
uint32_t ch_rb_flag = 1;
uint32_t ch_type;
uint64_t ch_cat_opt0 = 0;
uint64_t ch_cat_opt1 = 0;
uint32_t ch_timer_delay = 0;
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) == 0)
return (1);
if (pe_big_rbuf_pool_init(chp) == 0)
return (1);
if (board_info(chp)->caps & SUPPORTED_10000baseT_Full) {
(void) t1_os_pci_read_config_4(chp, A_PCICFG_PCIX_CMD,
&pcix_cmd);
if (chp->ch_config.burstsize_set) {
pcix_cmd &= ~0xc0000;
pcix_cmd |= (chp->ch_config.burstsize << 18);
}
if (chp->ch_config.transaction_cnt_set) {
pcix_cmd &= ~ 0x700000;
pcix_cmd |= (chp->ch_config.transaction_cnt << 20);
}
pcix_cmd |= (chp->ch_config.relaxed_ordering << 17);
(void) t1_os_pci_write_config_4(chp, A_PCICFG_PCIX_CMD,
pcix_cmd);
}
if (enable_latency_timer) {
if (board_info(chp)->caps & SUPPORTED_10000baseT_Full) {
(void) t1_os_pci_write_config_4(chp, 0xc, 0xf800);
}
}
update_mtu_tab(chp);
t1_interrupts_clear(chp);
return_val = 0;
return (return_val);
}
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));
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;
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;
p_config->link_speed = ch_link_speed;
p_config->num_of_ports = ch_num_of_ports;
p_config->cat_opt0 = ch_cat_opt0;
p_config->cat_opt1 = ch_cat_opt1;
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);
}
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;
rbp->cs_next = sa->ch_small_esb_free;
sa->ch_small_esb_free = rbp;
rbp->cs_owner = sa->ch_small_owner;
sa->ch_small_owner = rbp;
}
return (1);
error:
sa->ch_small_owner = NULL;
pe_rbuf_pool_free(sa);
return (0);
}
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;
rbp->cs_owner = sa->ch_big_owner;
sa->ch_big_owner = rbp;
}
return (1);
error:
sa->ch_big_owner = NULL;
pe_rbuf_pool_free(sa);
return (0);
}
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);
}
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);
}
void
pe_rbuf_pool_free(ch_t *sa)
{
ch_esb_t *rbp;
mutex_enter(&sa->ch_small_esbl);
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) {
sa->ch_small_esb_free = rbp->cs_next;
ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah);
kmem_free(rbp, sizeof (ch_esb_t));
}
mutex_exit(&sa->ch_small_esbl);
mutex_destroy(&sa->ch_small_esbl);
mutex_enter(&sa->ch_big_esbl);
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) {
sa->ch_big_esb_free = rbp->cs_next;
ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah);
kmem_free(rbp, sizeof (ch_esb_t));
}
mutex_exit(&sa->ch_big_esbl);
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;
ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah);
i = rbp->cs_index;
kmem_free(rbp, sizeof (ch_esb_t));
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);
atomic_dec_32(&buffers_in_use[rbp->cs_index]);
}
void
ch_big_rbuf_recycle(ch_esb_t *rbp)
{
ch_t *sa = rbp->cs_sa;
if (rbp->cs_flag) {
uint32_t i;
ch_free_dma_mem(rbp->cs_dh, rbp->cs_ah);
i = rbp->cs_index;
kmem_free(rbp, sizeof (ch_esb_t));
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);
atomic_dec_32(&buffers_in_use[rbp->cs_index]);
}
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);
}
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);
pe_rbuf_pool_free(sa);
}
}
static void
ext_intr_task(ch_t *adapter)
{
u32 enable;
(void) elmer0_ext_intr_handler(adapter);
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;
}
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);
}
}
#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];
char fa_src[6];
ushort_t fa_typ;
ushort_t fa_hrd;
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;
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);
cpl = (struct cpl_tx_pkt *)bp->b_rptr;
cpl->opcode = CPL_TX_PKT;
cpl->iff = 0;
cpl->ip_csum_dis = 1;
cpl->l4_csum_dis = 1;
cpl->vlan_valid = 0;
fap = (fake_arp_t *)&bp->b_rptr[SZ_CPL_TX_PKT];
bcopy(arpp, fap, sizeof (*fap));
bcopy(buf, fap->fa_dst, 6);
chp->ch_ip = fap->fa_src_ip;
bcopy(buf, fap->fa_dst_mac, 6);
bp->b_wptr = bp->b_rptr + sizeof (struct fake_arp)+SZ_CPL_TX_PKT;
sge_add_fake_arp(sge, (void *)bp);
return (0);
}
void
pe_free_fake_arp(void *arp)
{
mblk_t *bp = (mblk_t *)(arp);
freemsg(bp);
}
static uint32_t
pe_get_ip(unsigned char *arpp)
{
fake_arp_t fap;
bcopy(arpp, &fap, sizeof (fap));
return (fap.fa_src_ip);
}
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);
cmn_err(CE_NOTE, "%s: link is up", adapter_name(obj));
} else {
gld_linkstate(macinfo, GLD_LINKSTATE_DOWN);
cmn_err(CE_NOTE, "%s: link is down", adapter_name(obj));
}
}