From: Maxim Uvarov Date: Thu, 10 May 2012 20:28:36 +0000 (-0700) Subject: sxge/sxgevf: add new driver X-Git-Tag: v2.6.39-400.9.0~423^2~42 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=db73d236a589c4a1971e9b8d91bea4e80b4d8dee;p=users%2Fjedix%2Flinux-maple.git sxge/sxgevf: add new driver Orabug: 13444150 Signed-off-by: Maxim Uvarov --- diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 003116b8bb2ff..ee99d2d751621 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2929,6 +2929,9 @@ source "drivers/net/benet/Kconfig" source "drivers/net/hxge/Kconfig" +source "drivers/net/sxge/Kconfig" +source "drivers/net/sxgevf/Kconfig" + endif # NETDEV_10000 source "drivers/net/tokenring/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5add04207dd1f..9fccee4fded77 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -39,6 +39,8 @@ obj-$(CONFIG_BE2NET) += benet/ obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_BNA) += bna/ obj-$(CONFIG_HXGE) += hxge/ +obj-$(CONFIG_SXGE) += sxge/ +obj-$(CONFIG_SXGEVF) += sxgevf/ gianfar_driver-objs := gianfar.o \ gianfar_ethtool.o \ gianfar_sysfs.o diff --git a/drivers/net/sxge/Kconfig b/drivers/net/sxge/Kconfig new file mode 100644 index 0000000000000..ec60bce1e93d8 --- /dev/null +++ b/drivers/net/sxge/Kconfig @@ -0,0 +1,6 @@ +config SXGE + tristate "SXGE SOL ethernet driver " + depends on PCI + ---help--- + This driver supports the SXGE SOL 40Gb Ethernet driver. + diff --git a/drivers/net/sxge/Makefile b/drivers/net/sxge/Makefile new file mode 100644 index 0000000000000..ab1dd822a99f4 --- /dev/null +++ b/drivers/net/sxge/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SXGE) += sxge.o diff --git a/drivers/net/sxge/sxge.c b/drivers/net/sxge/sxge.c new file mode 100644 index 0000000000000..f522b41f01d7b --- /dev/null +++ b/drivers/net/sxge/sxge.c @@ -0,0 +1,3935 @@ +/* sxge.c: SOL ethernet driver + * + * Copyright (C) 2011 Oracle Corp. + */ +/* #pragma ident "@(#)sxge.c 1.60 12/03/23 SMI" */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sxge.h" + +#ifdef SXGE_VF +#define DRV_MODULE_NAME "sxgevf" +#else +#define DRV_MODULE_NAME "sxge" +#endif +#define DRV_MODULE_VERSION "0.03302012" +#define DRV_MODULE_RELDATE "March 30, 2012" + +static char version[] __devinitdata = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + +MODULE_AUTHOR("Oracle Corp (joyce.yu@oracle.com)"); +#ifdef SXGE_VF +MODULE_DESCRIPTION("SXGEVF ethernet driver"); +#else +MODULE_DESCRIPTION("SXGE ethernet driver"); +#endif +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +#ifndef readq +static u64 readq(void __iomem *reg) +{ + return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); +} + +static void writeq(u64 val, void __iomem *reg) +{ + writel(val & 0xffffffff, reg); + writel(val >> 32, reg + 0x4UL); +} +#endif + +static DEFINE_PCI_DEVICE_TABLE(sxge_pci_tbl) = { +#ifdef SXGE_VF + {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0x207b)}, +#else + {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0x2078)}, + {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0x207a)}, +#endif + {} +}; + +MODULE_DEVICE_TABLE(pci, sxge_pci_tbl); + +#define SXGE_TX_TIMEOUT (5 * HZ) + +#define SXGE_GET64(reg) readq(sxgep->regs + (reg)) +#define SXGE_PUT64(reg, val) writeq((val), sxgep->regs + (reg)) +#define SXGE_DBG printk + +#define SXGE_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) + +static int sxge_debug; +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "SXGE debug level"); + +static void sxge_ldg_rearm(struct sxge *sxgep, struct sxge_ldg *lp, int on) +{ + u64 val = (u64) (lp->timer << LDG_IMGMT_TIMER_SHIFT); + + if (on) + val |= LDG_IMGMT_ARM; + + SXGE_PUT64(LDG_IMGMT(lp->ldg_num), val); +} + +static int sxge_ldn_irq_enable(struct sxge *sxgep, int ldn, int on) +{ + u64 val; + + if (ldn < 0 || ldn > LDN_MAX) + return -EINVAL; + + val = SXGE_GET64(LD_MSK_GNUM(sxgep->vni, sxgep->intmgmt_nf, ldn)); + val &= ~LD_MSK_GNUM_EN_LDG_WR; + if (on) + val &= ~LD_MSK_GNUM_LDG_MSK; + else + val |= LD_MSK_GNUM_LDG_MSK; + SXGE_PUT64(LD_MSK_GNUM(sxgep->vni, sxgep->intmgmt_nf, ldn), + val | LD_MSK_GNUM_EN_MSK_WR); + + return 0; +} + +static int sxge_enable_ldn_in_ldg(struct sxge *sxgep, + struct sxge_ldg *lp, int on) +{ + int i; + + for (i = 0; i <= LDN_MAX; i++) { + int err; + + if (sxgep->ldg_map[i] != lp->ldg_num) + continue; + + err = sxge_ldn_irq_enable(sxgep, i, on); + if (err) + return err; + } + return 0; +} + +static int sxge_enable_interrupts(struct sxge *sxgep, int on) +{ + int i; + + for (i = 0; i < sxgep->num_ldg; i++) { + struct sxge_ldg *lp = &sxgep->ldg[i]; + int err; + + err = sxge_enable_ldn_in_ldg(sxgep, lp, on); + if (err) + return err; + } + for (i = 0; i < sxgep->num_ldg; i++) + sxge_ldg_rearm(sxgep, &sxgep->ldg[i], on); + + return 0; +} + +static void sxge_rx_skb_append(struct sk_buff *skb, struct page *page, + u32 offset, u32 size) +{ + int i = skb_shinfo(skb)->nr_frags; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + frag->page = page; + frag->page_offset = offset; + frag->size = size; + + skb->len += size; + skb->data_len += size; + skb->truesize += size; + + skb_shinfo(skb)->nr_frags = i + 1; +} + +static struct page *sxge_find_rxpage(struct rx_ring_info *rxringp, + u64 buf_index) +{ + struct page *p; + + p = rxringp->rxpage[buf_index]; + + return p; +} + +static void sxge_save_page_info(struct rx_ring_info *rxringp, + struct page *page, u64 base, int idx) +{ + page->index = base; + rxringp->rxpage[idx] = page; + rxringp->saved_base[idx] = base; +} + +static int sxge_rbr_add_page(struct sxge *sxgep, struct rx_ring_info *rxringp, + gfp_t mask, int start_index) +{ + struct page *page; + u64 addr; + int i; + + page = alloc_page(mask); + if (!page) + return -ENOMEM; + + addr = sxgep->ops->map_page(sxgep->device, page, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + + if (rxringp->rbr_blocks_per_page > 1) + atomic_add(rxringp->rbr_blocks_per_page - 1, + &compound_head(page)->_count); + + for (i = 0; i < rxringp->rbr_blocks_per_page; i++) { + __le64 *rbr = &rxringp->rbr[start_index + i]; + u64 buf_index = (u64)(start_index + i); + + sxge_save_page_info(rxringp, page, addr, start_index + i); + *rbr = (buf_index << RBR_DESCR_INDEX_SHIFT) | + cpu_to_le64(addr >> RBR_DESCR_ADDR_SHIFT); + addr += rxringp->rbr_block_size; + } + + return 0; +} + +static int sxge_rbr_refill(struct sxge *sxgep, struct rx_ring_info *rxringp, + gfp_t mask) +{ + int index = rxringp->rbr_index; + + rxringp->rbr_pending++; + if ((rxringp->rbr_pending % rxringp->rbr_blocks_per_page) == 0) { + int err = sxge_rbr_add_page(sxgep, rxringp, mask, index); + + if (unlikely(err)) { + rxringp->rbr_pending--; + return -1; + } + + rxringp->rbr_index += rxringp->rbr_blocks_per_page; + if (rxringp->rbr_index == rxringp->rbr_table_size) + rxringp->rbr_index = 0; + + if (rxringp->rbr_pending >= rxringp->rbr_kick_thresh) { + if ((rxringp->rbr_tail + rxringp->rbr_pending) >= + rxringp->rbr_table_size) { + rxringp->rbr_tail_wrap ^= + RDC_KICK_RBR_TAIL_WRAP; + rxringp->rbr_tail = rxringp->rbr_tail + + rxringp->rbr_pending - + rxringp->rbr_table_size; + } else { + rxringp->rbr_tail = rxringp->rbr_tail + + rxringp->rbr_pending; + } + SXGE_PUT64(RDC_KICK(rxringp->rdc_base), + RDC_KICK_RBR_TAIL_UP_VLD | + rxringp->rbr_tail_wrap | + (rxringp->rbr_tail & RDC_KICK_RBR_TAIL)); + rxringp->rbr_pending = 0; + } + } + + return 0; +} + +static int sxge_rx_pkt_ignore(struct sxge *sxgep, + struct rx_ring_info *rxringp, u32 pending_rcr) +{ + unsigned int index = rxringp->rcr_index; + int num_rcr = 0; + + rxringp->rx_dropped++; + while (1) { + struct page *page; + u64 buf_index, val, val_next; + u32 index_next; + + val = le64_to_cpup(&rxringp->rcr[index]); + if ((pending_rcr == 2) && (val & RCR_ENTRY_MULTI)) { + index_next = NEXT_RCR(rxringp, index); + val_next = le64_to_cpup(&rxringp->rcr[index_next]); + + if (val_next & RCR_ENTRY_MULTI) + return num_rcr; + } + + if (pending_rcr == 1) { + if (val & RCR_ENTRY_MULTI) + return num_rcr; + } + + num_rcr++; + if (num_rcr > pending_rcr) { + netdev_err(sxgep->dev, "%s(): Try to process haven't " + "RX pkt num_rcr 0x%x, pending_rcr 0x%x\n", + __func__, num_rcr, pending_rcr); + break; + } + + buf_index = val & RCR_ENTRY_INDEX; + page = sxge_find_rxpage(rxringp, buf_index); + + if (val & RCR_ENTRY_LAST_PKT_PER_BUF) { + sxgep->ops->unmap_page(sxgep->device, page->index, + PAGE_SIZE, DMA_FROM_DEVICE); + page->index = 0; + __free_page(page); + rxringp->rxpage[buf_index] = NULL; + rxringp->rbr_refill_pending++; + } + + index = NEXT_RCR(rxringp, index); + if (!(val & RCR_ENTRY_MULTI)) + break; + + } + + if ((index == 0) && + (rxringp->rcr_index == (rxringp->rcr_table_size - 1))) { + rxringp->rcr_head_wrap ^= RDC_KICK_RCR_HEAD_WRAP; + } else if (index < rxringp->rcr_index) { + rxringp->rcr_head_wrap ^= RDC_KICK_RCR_HEAD_WRAP; + } + rxringp->rcr_index = index; + + return num_rcr; +} + +static int sxge_process_rx_pkt(struct napi_struct *napi, struct sxge *sxgep, + struct rx_ring_info *rxringp, u32 pending_rcr) +{ + unsigned int index = rxringp->rcr_index; + struct sk_buff *skb; + int len, num_rcr; + + skb = netdev_alloc_skb(sxgep->dev, RX_SKB_ALLOC_SIZE); + if (unlikely(!skb)) + return sxge_rx_pkt_ignore(sxgep, rxringp, pending_rcr); + + num_rcr = 0; + while (1) { + struct page *page; + u32 rcr_size, append_size, index_next; + u64 buf_index, val, val_next, off; + + val = le64_to_cpup(&rxringp->rcr[index]); + if ((pending_rcr == 2) && (val & RCR_ENTRY_MULTI)) { + index_next = NEXT_RCR(rxringp, index); + val_next = le64_to_cpup(&rxringp->rcr[index_next]); + + if (val_next & RCR_ENTRY_MULTI) { + dev_kfree_skb(skb); + return num_rcr; + } + } + + if (pending_rcr == 1) { + if (val & RCR_ENTRY_MULTI) { + dev_kfree_skb(skb); + return num_rcr; + } + } + + num_rcr++; + if (num_rcr > pending_rcr) { + netdev_err(sxgep->dev, "%s(): Try to process haven't " + "RX pkt num_rcr 0x%x, pending_rcr 0x%x\n", + __func__, num_rcr, pending_rcr); + break; + } + + len = (val & RCR_ENTRY_PKT_SEG_LEN) >> + RCR_ENTRY_PKT_SEG_LEN_SHIFT; + + buf_index = val & RCR_ENTRY_INDEX; + page = sxge_find_rxpage(rxringp, buf_index); + + rcr_size = rxringp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >> + RCR_ENTRY_PKTBUFSZ_SHIFT]; + + off = ((val & RCR_ENTRY_SUBINDEX) >> RCR_ENTRY_SUBINDEX_SHIFT) * + rcr_size; + append_size = rcr_size; + if (num_rcr == 1) { + u64 class_code; + + class_code = (val & RCR_ENTRY_PKT_CLASS_CODE) >> + RCR_ENTRY_PKT_CLASS_CODE_SHIFT; + if ((class_code & CLS_CODE_TCP_UDP) && + !(val & RCR_ENTRY_PKT_ERR)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else + skb_checksum_none_assert(skb); + } + + if (!(val & RCR_ENTRY_MULTI)) + append_size = len; + + sxge_rx_skb_append(skb, page, off, append_size); + if (val & RCR_ENTRY_LAST_PKT_PER_BUF) { + sxgep->ops->unmap_page(sxgep->device, page->index, + PAGE_SIZE, DMA_FROM_DEVICE); + page->index = 0; + rxringp->rxpage[buf_index] = NULL; + rxringp->rbr_refill_pending++; + } else + get_page(page); + + index = NEXT_RCR(rxringp, index); + if (!(val & RCR_ENTRY_MULTI)) + break; + } + + if ((index == 0) && + (rxringp->rcr_index == (rxringp->rcr_table_size - 1))) { + rxringp->rcr_head_wrap ^= RDC_KICK_RCR_HEAD_WRAP; + } else if (index < rxringp->rcr_index) { + rxringp->rcr_head_wrap ^= RDC_KICK_RCR_HEAD_WRAP; + } + rxringp->rcr_index = index; + + skb_reserve(skb, NET_IP_ALIGN); + __pskb_pull_tail(skb, min(skb->len, (uint) VLAN_ETH_HLEN)); + + rxringp->rx_packets++; + rxringp->rx_bytes += skb->len; + + skb->protocol = eth_type_trans(skb, sxgep->dev); + skb_record_rx_queue(skb, rxringp->rx_channel); + napi_gro_receive(napi, skb); + + return num_rcr; +} + +static int release_tx_packet(struct sxge *sxgep, struct tx_ring_info *txringp, + int idx) +{ + struct tx_buff_info *tb = &txringp->tx_buffs[idx]; + struct sk_buff *skb = tb->skb; + struct tx_pkt_hdr *tp; + u64 tx_flags; + int i, len; + + if (!skb) { + netdev_err(sxgep->dev, "%s(): NULL SKB\n", __func__); + return idx; + } + + len = skb_headlen(skb); + txringp->tx_packets++; + + if (txringp->tdc_prsr_en != 1) { + tp = (struct tx_pkt_hdr *) skb->data; + tx_flags = le64_to_cpup(&tp->flags); + + txringp->tx_bytes += (((tx_flags & TXHDR_LEN) >> + TXHDR_LEN_SHIFT) - ((tx_flags & TXHDR_PAD) / 2)); + } else { + txringp->tx_bytes += len; + } + + sxgep->ops->unmap_single(sxgep->device, tb->mapping, + len, DMA_TO_DEVICE); + + if (le64_to_cpu(txringp->descr[idx]) & TX_DESC_MARK) + txringp->mark_pending--; + + tb->skb = NULL; + do { + idx = NEXT_TX(txringp, idx); + len -= MAX_TX_DESC_LEN; + } while (len > 0); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + tb = &txringp->tx_buffs[idx]; + BUG_ON(tb->skb != NULL); + sxgep->ops->unmap_page(sxgep->device, tb->mapping, + skb_shinfo(skb)->frags[i].size, + DMA_TO_DEVICE); + txringp->tx_bytes += skb_shinfo(skb)->frags[i].size; + idx = NEXT_TX(txringp, idx); + } + + dev_kfree_skb(skb); + + return idx; +} + +#define SXGE_TX_WAKEUP_THRESH(txringp) ((txringp)->pending / 4) + +static void sxge_tx_work(struct sxge *sxgep, struct tx_ring_info *txringp) +{ + struct netdev_queue *txq; + u16 pkt_cnt, tmp; + int cons, index; + u64 cs; + + index = (txringp - sxgep->tx_rings); + txq = netdev_get_tx_queue(sxgep->dev, index); + + cs = txringp->tx_cs; + if (unlikely(!(cs & (TDC_CS_MK | TDC_CS_MMK)))) + goto out; + + tmp = pkt_cnt = (cs & TDC_CS_PKT_CNT) >> TDC_CS_PKT_CNT_SHIFT; + pkt_cnt = (pkt_cnt - txringp->last_pkt_cnt) & + (TDC_CS_PKT_CNT >> TDC_CS_PKT_CNT_SHIFT); + + txringp->last_pkt_cnt = tmp; + + cons = txringp->cons; + + while (pkt_cnt--) + cons = release_tx_packet(sxgep, txringp, cons); + + txringp->cons = cons; + smp_mb(); + +out: + if (unlikely(netif_tx_queue_stopped(txq) && + (sxge_tx_avail(txringp) > SXGE_TX_WAKEUP_THRESH(txringp)))) { + __netif_tx_lock(txq, smp_processor_id()); + if (netif_tx_queue_stopped(txq) && + (sxge_tx_avail(txringp) > + SXGE_TX_WAKEUP_THRESH(txringp))) + netif_tx_wake_queue(txq); + __netif_tx_unlock(txq); + } +} + +static int sxge_rx_work(struct napi_struct *napi, struct sxge *sxgep, + struct rx_ring_info *rxringp, int budget) +{ + int qlen = 0, rcr_done = 0, work_done = 0; + struct rxdma_mailbox *mbox = rxringp->mbox; + u64 stat = 0, kick = 0, qlen_seg = 0; + u64 rcrhead_index; + + /* flush RCR to force the update */ + SXGE_PUT64(RDC_FLSH(rxringp->rdc_base), 0x1); + + kick = SXGE_GET64(RDC_KICK(rxringp->rdc_base)); + stat = SXGE_GET64(RDC_CTL_STAT(rxringp->rdc_base)); + rcrhead_index = (u64) rxringp->rcr_index; + rxringp->rcr_tail_wrap = stat & RDC_CTL_STAT_TAIL_WRAP; + + netif_printk(sxgep, rx_status, KERN_DEBUG, sxgep->dev, + "%s(chan[%d]), stat[%llx] kick[%llx]\n", + __func__, rxringp->rx_channel, stat, kick); + + if ((rxringp->rcr_tail_wrap >> RDC_CTL_STAT_TAIL_WRAP_SHIFT) != + (rxringp->rcr_head_wrap >> RDC_KICK_RCR_HEAD_WRAP_SHIFT)) + qlen_seg = rxringp->rcr_table_size - rcrhead_index + + ((stat & RDC_CTL_STAT_TAIL) >> RDC_CTL_STAT_TAIL_SHIFT); + else + qlen_seg = ((stat & RDC_CTL_STAT_TAIL) >> + RDC_CTL_STAT_TAIL_SHIFT) - rcrhead_index; + + mbox->rx_dma_ctl_stat = 0; + mbox->rbr_tail_rcr_head = 0; + + rcr_done = work_done = 0; + qlen = (u32) qlen_seg; + qlen = min(qlen, budget); + while (rcr_done < qlen) { + u32 rcr_num; + + rcr_num = sxge_process_rx_pkt(napi, sxgep, rxringp, + qlen - rcr_done); + if (!rcr_num) + break; + rcr_done += rcr_num; + work_done++; + } + + if (rxringp->rbr_refill_pending >= rxringp->rbr_kick_thresh) { + unsigned int i, err, refill_pending_left = 0; + + for (i = 0; i < rxringp->rbr_refill_pending; i++) { + err = sxge_rbr_refill(sxgep, rxringp, GFP_ATOMIC); + if (err) + refill_pending_left++; + } + if (refill_pending_left) { + rxringp->rbr_refill_pending = refill_pending_left; + pr_info("No memeory to refill buffers " + "stat 0x%llx, kick 0x%llx, " + "rcrhead_index 0x%llx\n", + stat, kick, rcrhead_index); + } else + rxringp->rbr_refill_pending = 0; + } + + if (rcr_done) + SXGE_PUT64(RDC_KICK(rxringp->rdc_base), + RDC_KICK_RCR_HEAD_UP_VLD | rxringp->rcr_head_wrap | + ((rxringp->rcr_index << RDC_KICK_RCR_HEAD_PT_SHIFT) & + RDC_KICK_RCR_HEAD_PT)); + + if (stat & RDC_CTL_STAT_PKTCNT_OVERFLOW) + rxringp->rx_hw_pktcnt += + SXGE_GET64(RDC_PKTCNT(rxringp->rdc_base)) & + RDC_PKTCNT_COUNT; + if (stat & RDC_CTL_STAT_DROPCNT_OVERFLOW) + rxringp->rx_hw_pktdrop += + SXGE_GET64(RDC_PKTDROP(rxringp->rdc_base)) & + RDC_PKTDROP_COUNT; + if (stat & RDC_CTL_STAT_RBR_EMPTY) + rxringp->rx_rbr_empty++; + + if (!rxringp->rbr_refill_pending) { + SXGE_PUT64(RDC_CTL_STAT(rxringp->rdc_base), + stat & RDC_CTL_STAT_WRITE_CLEAR_INT); + } else { + SXGE_PUT64(RDC_CTL_STAT(rxringp->rdc_base), + (stat & RDC_CTL_STAT_WRITE_CLEAR_INT) & + ~RDC_CTL_STAT_RBR_EMPTY); + } + + return work_done; +} + +static int sxge_poll_core(struct sxge *sxgep, struct sxge_ldg *lp, int budget) +{ + u64 v = lp->v; + u32 tx_vec = (v >> 4) & 0xf; + u32 rx_vec = (v & 0xf); + int i, work_done = 0; + u8 vni = sxgep->vni, intmgmt_nf = sxgep->intmgmt_nf; + u64 val; + + netif_printk(sxgep, intr, KERN_DEBUG, sxgep->dev, + "%s() v[%016llx]\n", __func__, (unsigned long long)v); + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + if (tx_vec & (1 << txringp->tx_channel)) + sxge_tx_work(sxgep, txringp); + val = SXGE_GET64(LD_MSK_GNUM(vni, intmgmt_nf, + LDN_TXDMA(txringp->tx_channel))); + val &= ~LD_MSK_GNUM_LDG_MSK; + SXGE_PUT64(LD_MSK_GNUM(vni, intmgmt_nf, + LDN_TXDMA(txringp->tx_channel)), + val | LD_MSK_GNUM_EN_MSK_WR); + } + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + if (rx_vec & (1 << rxringp->rx_channel)) { + int this_work_done; + + this_work_done = sxge_rx_work(&lp->napi, sxgep, + rxringp, budget); + + budget -= this_work_done; + work_done += this_work_done; + } + val = SXGE_GET64(LD_MSK_GNUM(vni, intmgmt_nf, + LDN_RXDMA(rxringp->rx_channel))); + val &= ~LD_MSK_GNUM_LDG_MSK; + SXGE_PUT64(LD_MSK_GNUM(vni, intmgmt_nf, + LDN_RXDMA(rxringp->rx_channel)), + val | LD_MSK_GNUM_EN_MSK_WR); + } + + return work_done; +} + +static int sxge_poll(struct napi_struct *napi, int budget) +{ + struct sxge_ldg *lp = container_of(napi, struct sxge_ldg, napi); + struct sxge *sxgep = lp->sxgep; + int work_done = 0; + + work_done = sxge_poll_core(sxgep, lp, budget); + + if (work_done < budget) { + napi_complete(napi); + sxge_ldg_rearm(sxgep, lp, 1); + } + return work_done; +} + +static void sxge_log_rxchan_errors(struct sxge *sxgep, + struct rx_ring_info *rxringp, u64 stat) +{ + netdev_err(sxgep->dev, "RX channel %u errors ( ", rxringp->rx_channel); + + if (stat & RDC_CTL_STAT_REQ_REJECT) + pr_cont("REQ_REJECT "); + if (stat & RDC_CTL_STAT_RBR_TIMEOUT) + pr_cont("RBR_TIMEOUT "); + if (stat & RDC_CTL_STAT_RSP_DAT_ERR) + pr_cont("RSP_DAT_ERR "); + if (stat & RDC_CTL_STAT_RCR_ACK_ERR) + pr_cont("RCR_ACK_ERR "); + if (stat & RDC_CTL_STAT_RCR_SHA_PAR) + pr_cont("RCR_SHA_PAR "); + if (stat & RDC_CTL_STAT_RBR_PRE_PAR) + pr_cont("RBR_PRE_PAR "); + if (stat & RDC_CTL_STAT_RCR_UNDERFLOW) + pr_cont("RCR_UNDERFLOW "); + if (stat & RDC_CTL_STAT_RBR_OVERFLOW) + pr_cont("RBR_OVERFLOW "); + + pr_cont(")\n"); +} + +static int sxge_rx_error(struct sxge *sxgep, struct rx_ring_info *rxringp) +{ + u64 stat, err_log; + int err = 0; + + stat = SXGE_GET64(RDC_CTL_STAT(rxringp->rdc_base)); + err_log = SXGE_GET64(RDC_RNG_ERR_LOG(rxringp->rdc_base)); + + if (stat & RDC_CTL_STAT_CHAN_FATAL) + err = -EINVAL; + + if (err) { + netdev_err(sxgep->dev, "RX channel %u error, " + "stat[%llx] err_log[%llx]\n", + rxringp->rx_channel, + (unsigned long long) stat, + (unsigned long long) err_log); + + sxge_log_rxchan_errors(sxgep, rxringp, stat); + schedule_work(&sxgep->reset_task); + } + +#if 0 /* Can't clear it from the STAT reg, has to do it in the STAT_DBG */ + /* Will be fixed in the next ASIC? */ + if (stat & RDC_CTL_STAT_FIFO_ERR) + rxringp->rx_fifo_error++; + + if (stat & RDC_CTL_STAT_RCR_SHADOW_FULL) + rxringp->rx_rcr_shadow_full++; + + if (stat & RDC_CTL_STAT_WRITE_CLEAR_ERRS) + SXGE_PUT64(RDC_CTL_STAT_DBG(rxringp->rdc_base), 0); +#endif + + return err; +} + + +static void sxge_log_txchan_errors(struct sxge *sxgep, + struct tx_ring_info *txringp, u64 cs) +{ + netdev_err(sxgep->dev, "TX channel %u errors ( ", txringp->tx_channel); + + if (cs & TDC_CS_REJECT_RESP_ERR) + pr_cont("REJECT_RESP_ERR "); + if (cs & TDC_CS_SOP_BIT_ERR) + pr_cont("SOP_BIT_ERR "); + if (cs & TDC_CS_PREMATURE_SOP_ERR) + pr_cont("PREMATURE_SOP_ERR "); + if (cs & TDC_CS_DESC_LENGTH_ERR) + pr_cont("DESC_LENGTH_ERR "); + if (cs & TDC_CS_DESC_NUM_PTR_ERR) + pr_cont("DESC_NUM_PTR_ERR "); + if (cs & TDC_CS_MBOX_ERR) + pr_cont("MBOX_ERR "); + if (cs & TDC_CS_PKT_SIZE_ERR) + pr_cont("PKT_SIZE_ERR "); + if (cs & TDC_CS_TX_RING_OFLOW) + pr_cont("TX_RING_OFLOW "); + if (cs & TDC_CS_PREF_BUF_PAR_ERR) + pr_cont("PREF_BUF_PAR_ERR "); + if (cs & TDC_CS_NACK_PREF) + pr_cont("NACK_PREF "); + if (cs & TDC_CS_NACK_PKT_RD) + pr_cont("NACK_PKT_RD "); + if (cs & TDC_CS_CONF_PART_ERR) + pr_cont("CONF_PART_ERR "); + if (cs & TDC_CS_PKT_PRT_ERR) + pr_cont("PKT_PRT_ERR "); + + pr_cont(")\n"); +} + +static int sxge_tx_error(struct sxge *sxgep, struct tx_ring_info *txringp) +{ + u64 cs, logh, logl; + + cs = SXGE_GET64(TDC_CS(txringp->tdc_base)); + logh = SXGE_GET64(TDC_RNG_ERR_LOGH(txringp->tdc_base)); + logl = SXGE_GET64(TDC_RNG_ERR_LOGL(txringp->tdc_base)); + + netdev_err(sxgep->dev, "TX channel %u error, " + "cs[%llx] logh[%llx] logl[%llx]\n", + txringp->tx_channel, + (unsigned long long) cs, + (unsigned long long) logh, + (unsigned long long) logl); + + sxge_log_txchan_errors(sxgep, txringp, cs); + schedule_work(&sxgep->reset_task); + + return -ENODEV; +} + +static int sxge_txvmac_interrupt(struct sxge *sxgep) +{ + struct sxge_vmac_stats *vmp = &sxgep->vmac_stats; + u64 val; + + val = SXGE_GET64(TXVMAC_STAT(sxgep->vni, sxgep->vmac)); + if (val & TXVMAC_STAT_TX_BYTE_CNT_OVL) { + vmp->txvmac_byte_cnt_ovl++; + vmp->txvmac_frames += SXGE_GET64( + TXVMAC_FRM_CNT(sxgep->vni, sxgep->vmac)); + } + if (val & TXVMAC_STAT_TX_FRAME_CNT_OVL) { + vmp->txvmac_frame_cnt_ovl++; + vmp->txvmac_bytes += SXGE_GET64( + TXVMAC_BYT_CNT(sxgep->vni, sxgep->vmac)); + } + + SXGE_PUT64(TXVMAC_STAT(sxgep->vni, sxgep->vmac), + (val & TXVMAC_STAT_SW_RST_DONE) | + (val & TXVMAC_STAT_TX_BYTE_CNT_OVL) | + (val & TXVMAC_STAT_TX_FRAME_CNT_OVL)); + + return 0; +} + +static int sxge_rxvmac_interrupt(struct sxge *sxgep) +{ + struct sxge_vmac_stats *vmp = &sxgep->vmac_stats; + u64 val; + u8 vni = sxgep->vni, vmac = sxgep->vmac; + struct sxge_link_config *linkp = &sxgep->link_config; + struct net_device *dev = sxgep->dev; + + val = SXGE_GET64(RXVMAC_STAT(sxgep->vni, sxgep->vmac)); + if (val & RXVMAC_STAT_FRAME_CNT_OVL) { + vmp->rxvmac_frames_cnt_ovl++; + vmp->rxvmac_frames += + SXGE_GET64(RXVMAC_FRM_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_BYTE_CNT_OVL) { + vmp->rxvmac_byte_cnt_ovl++; + vmp->rxvmac_bytes += + SXGE_GET64(RXVMAC_BYT_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_DROP_CNT_OVL) { + vmp->rxvmac_drop_cnt_ovl++; + vmp->rxvmac_drops += + SXGE_GET64(RXVMAC_DROP_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_DROP_BYTE_OVL) { + vmp->rxvmac_drop_byte_ovl++; + vmp->rxvmac_drop_bytes += + SXGE_GET64(RXVMAC_DROPBYT_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_MCAST_FRAME_CNT_OVL) { + vmp->rxvmac_mcast_frame_cnt_ovl++; + vmp->rxvmac_mcasts += + SXGE_GET64(RXVMAC_MCAST_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_BCAST_FRAME_CNT_OVL) { + vmp->rxvmac_bcast_frame_cnt_ovl++; + vmp->rxvmac_bcasts += + SXGE_GET64(RXVMAC_BCAST_CNT(vni, vmac)); + } + if (val & RXVMAC_STAT_LINK_UP) { + vmp->rxvmac_link_up = 1; + vmp->rxvmac_link_down = 0; + } + if (val & RXVMAC_STAT_LINK_DOWN) { + vmp->rxvmac_link_down = 1; + vmp->rxvmac_link_up = 0; + } + if (val & RXVMAC_STAT_LINK_STATE) { + vmp->rxvmac_link_state = 1; + vmp->rxvmac_link_up = 1; + vmp->rxvmac_link_down = 0; + } else { + vmp->rxvmac_link_state = 0; + vmp->rxvmac_link_up = 0; + vmp->rxvmac_link_down = 1; + } + + if (!netif_carrier_ok(dev) && vmp->rxvmac_link_state) { + netif_info(sxgep, link, dev, "Link is up at %s\n", + (linkp->active_speed == SPEED_40000 ? + "40Gb/sec" : "10Gb/sec")); + netif_carrier_on(dev); + } else if (netif_carrier_ok(dev) && !vmp->rxvmac_link_state) { + netif_warn(sxgep, link, dev, "Link is down\n"); + netif_carrier_off(dev); + } + + SXGE_PUT64(RXVMAC_STAT(sxgep->vni, sxgep->vmac), + (val & RXVMAC_STAT_LINK_UP) | + (val & RXVMAC_STAT_LINK_DOWN)); + + return 0; +} + +static int sxge_mailbox_interrupt(struct sxge *sxgep) +{ + u64 val = SXGE_GET64(SXGE_MB_STAT); +#if 1 + netdev_info(sxgep->dev, "%s: mailbox_interrupt mb_stat 0x%llx\n", + __func__, val); +#endif + return 0; +} + +static int sxge_vni_error(struct sxge *sxgep) +{ +#if 1 + netdev_info(sxgep->dev, "%s: vni_error_interrupt\n", __func__); +#endif + return 0; +} + +static int sxge_slowpath_interrupt(struct sxge *sxgep, struct sxge_ldg *lp, + u64 v) +{ + int i, err = 0; + + lp->v = v; + + if (v & LDSV_V1_RXDMA) { + u32 rx_vec = (v >> 16) & 0xf; + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + if (rx_vec & (1 << rxringp->rx_channel)) { + int r = sxge_rx_error(sxgep, rxringp); + if (r) + err = r; + } + } + } + if (v & LDSV_V1_TXDMA) { + u32 tx_vec = (v >> 20) & 0xf; + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + if (tx_vec & (1 << txringp->tx_channel)) { + int r = sxge_tx_error(sxgep, txringp); + if (r) + err = r; + } + } + } + + + if (v & LDSV_V0_MAILBOX) { + int r = sxge_mailbox_interrupt(sxgep); + if (r) + err = r; + } + + if (v & LDSV_V0_RXVMAC) { + int r = sxge_rxvmac_interrupt(sxgep); + if (r) + err = r; + } + + if (v & LDSV_V0_TXVMAC) { + int r = sxge_txvmac_interrupt(sxgep); + if (r) + err = r; + } + + if (v & LDSV_V1_VNI_ERROR) { + int r = sxge_vni_error(sxgep); + if (r) + err = r; + } + + if (err) + sxge_enable_interrupts(sxgep, 0); + + return err; +} + +static void sxge_rxchan_intr(struct sxge *sxgep, struct rx_ring_info *rxringp, + int ldn) +{ +#if 0 + struct rxdma_mailbox *mbox = rxringp->mbox; + u64 stat_write, stat = le64_to_cpup(&mbox->rx_dma_ctl_stat); + + stat_write = (RDC_CTL_STAT_RCRTHRES | + RDC_CTL_STAT_RCRTO); + + SXGE_PUT64(RDC_CTL_STAT(rxringp->rdc_base), stat_write); + netif_printk(sxgep, intr, KERN_DEBUG, sxgep->dev, + "%s: rxchan_intr stat[%llx]\n", + __func__, (unsigned long long) stat); +#endif +} + +static void sxge_txchan_intr(struct sxge *sxgep, struct tx_ring_info *txringp, + int ldn) +{ + txringp->tx_cs = SXGE_GET64(TDC_CS(txringp->tdc_base)); + + netif_printk(sxgep, intr, KERN_DEBUG, sxgep->dev, "%s() cs[%llx]\n", + __func__, (unsigned long long)txringp->tx_cs); +} + +static void __sxge_fastpath_interrupt(struct sxge *sxgep, int ldg, u64 v) +{ + u32 rx_vec, tx_vec; + int i; + + tx_vec = (v >> 4) & 0xf; + rx_vec = (v & 0xf); + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + int ldn = LDN_RXDMA(rxringp->rx_channel); + + if (sxgep->ldg_map[ldn] != ldg) + continue; + + SXGE_PUT64(LD_MSK_GNUM(sxgep->vni, sxgep->intmgmt_nf, ldn), + LD_MSK_GNUM_LDG_MSK | LD_MSK_GNUM_EN_MSK_WR); + if (rx_vec & (1 << rxringp->rx_channel)) + sxge_rxchan_intr(sxgep, rxringp, ldn); + } + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + int ldn = LDN_TXDMA(txringp->tx_channel); + + if (sxgep->ldg_map[ldn] != ldg) + continue; + + SXGE_PUT64(LD_MSK_GNUM(sxgep->vni, sxgep->intmgmt_nf, ldn), + LD_MSK_GNUM_LDG_MSK | LD_MSK_GNUM_EN_MSK_WR); + if (tx_vec & (1 << txringp->tx_channel)) + sxge_txchan_intr(sxgep, txringp, ldn); + } +} + +static void sxge_schedule_napi(struct sxge *sxgep, struct sxge_ldg *lp, u64 v) +{ + if (likely(napi_schedule_prep(&lp->napi))) { + lp->v = v; + __sxge_fastpath_interrupt(sxgep, lp->ldg_num, v); + __napi_schedule(&lp->napi); + } +} + +static irqreturn_t sxge_interrupt(int irq, void *dev_id) +{ + struct sxge_ldg *lp = dev_id; + struct sxge *sxgep = lp->sxgep; + int ldg = lp->ldg_num; + unsigned long flags; + u64 v; + + if (netif_msg_intr(sxgep)) + printk(KERN_DEBUG KBUILD_MODNAME ": " "%s() ldg[%p](%d) ", + __func__, lp, ldg); + + spin_lock_irqsave(&sxgep->lock, flags); + + v = SXGE_GET64(LDSV(sxgep->vni, sxgep->intmgmt_nf, ldg)); + + if (netif_msg_intr(sxgep)) + pr_cont("v[%llx]\n", (unsigned long long) v); + + if (unlikely(!v)) { + spin_unlock_irqrestore(&sxgep->lock, flags); + return IRQ_NONE; + } + + if (unlikely(v & (LDSV_LDSV1_MASK | LDSV_V0_TXVMAC | + LDSV_V0_RXVMAC | LDSV_V0_MAILBOX))) { + int err = sxge_slowpath_interrupt(sxgep, lp, v); + if (err) + goto out; + } + + if (likely(v & (LDSV_V0_TXDMA | LDSV_V0_RXDMA))) + sxge_schedule_napi(sxgep, lp, v); + else + sxge_ldg_rearm(sxgep, lp, 1); + +out: + spin_unlock_irqrestore(&sxgep->lock, flags); + + return IRQ_HANDLED; +} + +static int sxge_alloc_rx_ring_info(struct sxge *sxgep, + struct rx_ring_info *rxringp) +{ + rxringp->rxpage = kzalloc(MAX_RBR_RING_SIZE * sizeof(struct page *), + GFP_KERNEL); + + if (!rxringp->rxpage) + return -ENOMEM; + + rxringp->mbox = sxgep->ops->alloc_coherent(sxgep->device, + sizeof(struct rxdma_mailbox), + &rxringp->mbox_dma, GFP_KERNEL); + if (!rxringp->mbox) + return -ENOMEM; + if ((unsigned long)rxringp->mbox & (64UL - 1)) { + netdev_err(sxgep->dev, "Coherent alloc gives misaligned " + "RXDMA mailbox %p\n", rxringp->mbox); + return -EINVAL; + } + + rxringp->rcr = sxgep->ops->alloc_coherent(sxgep->device, + MAX_RCR_RING_SIZE * sizeof(__le64), + &rxringp->rcr_dma, GFP_KERNEL); + if (!rxringp->rcr) + return -ENOMEM; + if ((unsigned long)rxringp->rcr & (64UL - 1)) { + netdev_err(sxgep->dev, "Coherent alloc gives misaligned " + "RXDMA RCR table %p\n", rxringp->rcr); + return -EINVAL; + } + rxringp->rcr_table_size = MAX_RCR_RING_SIZE; + rxringp->rcr_index = 0; + rxringp->rcr_head_wrap = 0; + rxringp->rcr_tail_wrap = 0; + + rxringp->rbr = sxgep->ops->alloc_coherent(sxgep->device, + MAX_RBR_RING_SIZE * sizeof(__le64), + &rxringp->rbr_dma, GFP_KERNEL); + if (!rxringp->rbr) + return -ENOMEM; + if ((unsigned long)rxringp->rbr & (64UL - 1)) { + netdev_err(sxgep->dev, "Coherent alloc gives misaligned " + "RXDMA RBR table %p\n", rxringp->rbr); + return -EINVAL; + } + rxringp->rbr_table_size = MAX_RBR_RING_SIZE; + rxringp->rbr_index = 0; + rxringp->rbr_pending = 0; + rxringp->rbr_tail = 0; + rxringp->rbr_tail_wrap = 0; + rxringp->rbr_head_wrap = 0; + + return 0; +} + +static void sxge_set_max_burst(struct sxge *sxgep, struct tx_ring_info *txringp) +{ + int mtu = sxgep->dev->mtu; + + /* These values are recommended by the HW designers for fair + * utilization of DRR amongst the rings. + */ + txringp->max_burst = mtu + 32; + if (txringp->max_burst > 4096) + txringp->max_burst = 4096; +} + +static int sxge_alloc_tx_ring_info(struct sxge *sxgep, + struct tx_ring_info *txringp) +{ + BUILD_BUG_ON(sizeof(struct txdma_mailbox) != 64); + + txringp->mbox = sxgep->ops->alloc_coherent(sxgep->device, + sizeof(struct txdma_mailbox), + &txringp->mbox_dma, GFP_KERNEL); + if (!txringp->mbox) + return -ENOMEM; + if ((unsigned long)txringp->mbox & (64UL - 1)) { + netdev_err(sxgep->dev, "Coherent alloc gives misaligned " + "TXDMA mailbox %p\n", txringp->mbox); + return -EINVAL; + } + + txringp->descr = sxgep->ops->alloc_coherent(sxgep->device, + MAX_TX_RING_SIZE * sizeof(__le64), + &txringp->descr_dma, GFP_KERNEL); + if (!txringp->descr) + return -ENOMEM; + if ((unsigned long)txringp->descr & (64UL - 1)) { + netdev_err(sxgep->dev, "Coherent alloc gives misaligned " + "TXDMA descr table %p\n", txringp->descr); + return -EINVAL; + } + + txringp->pending = MAX_TX_RING_SIZE; + txringp->prod = 0; + txringp->cons = 0; + txringp->wrap_bit = 0; + + /* XXX make these configurable... XXX */ + txringp->mark_freq = txringp->pending / 64; + + sxge_set_max_burst(sxgep, txringp); + + return 0; +} + +static void sxge_size_rbr(struct sxge *sxgep, struct rx_ring_info *rxringp) +{ + u16 bss; + + bss = min(PAGE_SHIFT, 15); + + rxringp->rbr_block_size = 1 << bss; + rxringp->rbr_blocks_per_page = 1 << (PAGE_SHIFT-bss); + + rxringp->rbr_sizes[0] = 256; + rxringp->rbr_sizes[1] = 1024; + if (sxgep->dev->mtu > ETH_DATA_LEN) { + switch (PAGE_SIZE) { + case 4 * 1024: + rxringp->rbr_sizes[2] = 4096; + break; + + default: + rxringp->rbr_sizes[2] = 8192; + break; + } + } else { + rxringp->rbr_sizes[2] = 2048; + } + rxringp->rbr_sizes[3] = rxringp->rbr_block_size; +} + +static int sxge_rbr_fill(struct sxge *sxgep, struct rx_ring_info *rxringp, + gfp_t mask) +{ + int blocks_per_page = rxringp->rbr_blocks_per_page; + int err, index = rxringp->rbr_index; + + err = 0; + while (index < (rxringp->rbr_table_size - blocks_per_page)) { + err = sxge_rbr_add_page(sxgep, rxringp, mask, index); + if (err) + break; + index += blocks_per_page; + } + + rxringp->rbr_index = index; + + return err; +} + +static void sxge_rbr_free(struct sxge *sxgep, struct rx_ring_info *rxringp) +{ + int i; + + for (i = 0; i < MAX_RBR_RING_SIZE; i++) { + struct page *page; + + page = rxringp->rxpage[i]; + if (page) { + u64 base = page->index; + + sxgep->ops->unmap_page(sxgep->device, + base, PAGE_SIZE, + DMA_FROM_DEVICE); + page->index = 0; + __free_page(page); + } + } + + for (i = 0; i < rxringp->rbr_table_size; i++) + rxringp->rbr[i] = cpu_to_le64(0); + rxringp->rbr_index = 0; + rxringp->rbr_tail = 0; + rxringp->rbr_tail_wrap = 0; + rxringp->rbr_head_wrap = 0; +} + +static void sxge_free_rx_ring_info(struct sxge *sxgep, + struct rx_ring_info *rxringp) +{ + if (rxringp->mbox) { + sxgep->ops->free_coherent(sxgep->device, + sizeof(struct rxdma_mailbox), + rxringp->mbox, rxringp->mbox_dma); + rxringp->mbox = NULL; + } + + if (rxringp->rcr) { + sxgep->ops->free_coherent(sxgep->device, + MAX_RCR_RING_SIZE * sizeof(__le64), + rxringp->rcr, rxringp->rcr_dma); + rxringp->rcr = NULL; + rxringp->rcr_table_size = 0; + rxringp->rcr_index = 0; + rxringp->rcr_head_wrap = 0; + rxringp->rcr_tail_wrap = 0; + } + + if (rxringp->rbr) { + sxge_rbr_free(sxgep, rxringp); + + sxgep->ops->free_coherent(sxgep->device, + MAX_RBR_RING_SIZE * sizeof(__le64), + rxringp->rbr, rxringp->rbr_dma); + rxringp->rbr = NULL; + rxringp->rbr_table_size = 0; + rxringp->rbr_index = 0; + rxringp->rbr_tail = 0; + rxringp->rbr_tail_wrap = 0; + rxringp->rbr_head_wrap = 0; + } + + kfree(rxringp->rxpage); + rxringp->rxpage = NULL; +} + +static void sxge_free_tx_ring_info(struct sxge *sxgep, + struct tx_ring_info *txringp) +{ + if (txringp->mbox) { + sxgep->ops->free_coherent(sxgep->device, + sizeof(struct txdma_mailbox), + txringp->mbox, txringp->mbox_dma); + txringp->mbox = NULL; + } + if (txringp->descr) { + int i; + + for (i = 0; i < MAX_TX_RING_SIZE; i++) { + if (txringp->tx_buffs[i].skb) + (void) release_tx_packet(sxgep, txringp, i); + } + + sxgep->ops->free_coherent(sxgep->device, + MAX_TX_RING_SIZE * sizeof(__le64), + txringp->descr, txringp->descr_dma); + txringp->descr = NULL; + txringp->pending = 0; + txringp->prod = 0; + txringp->cons = 0; + txringp->wrap_bit = 0; + } +} + +static void sxge_free_channels(struct sxge *sxgep) +{ + int i; + + if (sxgep->rx_rings) { + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + sxge_free_rx_ring_info(sxgep, rxringp); + } + kfree(sxgep->rx_rings); + sxgep->rx_rings = NULL; + } + + if (sxgep->tx_rings) { + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + sxge_free_tx_ring_info(sxgep, txringp); + } + kfree(sxgep->tx_rings); + sxgep->tx_rings = NULL; + } +} + +static int sxge_alloc_channels(struct sxge *sxgep) +{ + int i, j, err, ring_num = 0; + struct rx_ring_info *rx_rings; + struct tx_ring_info *tx_rings; + + err = -ENOMEM; + dev_printk(KERN_DEBUG, sxgep->device, "num_ring = %d\n", + sxgep->num_rings); + rx_rings = kcalloc(sxgep->num_rings, + sizeof(struct rx_ring_info), GFP_KERNEL); + if (!rx_rings) { + dev_printk(KERN_DEBUG, sxgep->device, + "kcalloc RX RING failed\n"); + return err; + } + + tx_rings = kcalloc(sxgep->num_rings, + sizeof(struct tx_ring_info), GFP_KERNEL); + if (!tx_rings) { + kfree(rx_rings); + dev_printk(KERN_DEBUG, sxgep->device, + "kcalloc TX RING failed\n"); + return err; + } + + smp_wmb(); + sxgep->rx_rings = rx_rings; + smp_wmb(); + sxgep->tx_rings = tx_rings; + + netif_set_real_num_rx_queues(sxgep->dev, sxgep->num_rings); + netif_set_real_num_tx_queues(sxgep->dev, sxgep->num_rings); + + /* + * Scan the RDAT table to get the VNI, DMA, VMAC numbers and calculate + * the register base + */ + for (i = 0; i < MAX_VNI_NUM; i++) { + if (sxgep->piobar_resource[i]) { + uint8_t piobar_vmac_resource, vmac = 0; + uint8_t piobar_dma_resource; + + piobar_dma_resource = sxgep->piobar_resource[i]; + piobar_vmac_resource = sxgep->piobar_resource[i] >> 4; + for (j = 0; j < 4; j++) { + if (piobar_dma_resource & 0x1) { + sxgep->rx_rings[ring_num].vni = i; + sxgep->rx_rings[ring_num].rdc_base = + RDC_BASE(i, j); + sxgep->rx_rings[ring_num].rx_channel = + j; + sxgep->tx_rings[ring_num].vni = i; + sxgep->tx_rings[ring_num].tdc_base = + TDC_BASE(i, j); + sxgep->tx_rings[ring_num].tx_channel = + j; + switch (piobar_vmac_resource & 0xf) { + case 0x1: + vmac = 0; + break; + case 0x2: + vmac = 1; + break; + case 0x4: + vmac = 2; + break; + case 0x8: + vmac = 3; + break; + default: + netdev_warn(sxgep->dev, "%s: " + "invalid vmac assigned\n", + __func__); + goto out_err; + } + sxgep->rx_rings[ring_num].vmac = vmac; + sxgep->rx_rings[ring_num].vmac_base = + VMAC_BASE(i, vmac); + sxgep->tx_rings[ring_num].vmac = vmac; + sxgep->tx_rings[ring_num].vmac_base = + VMAC_BASE(i, vmac); + sxgep->vmac = vmac; + ring_num++; + } + piobar_dma_resource = piobar_dma_resource >> 1; + } + } + } + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + rxringp->sxgep = sxgep; + + /* rxings */ + err = sxge_alloc_rx_ring_info(sxgep, rxringp); + if (err) + goto out_err; + + sxge_size_rbr(sxgep, rxringp); + /* Change from 16 to 1, may change it back */ + rxringp->rcr_pkt_threshold = 1; + rxringp->rcr_timeout = 8; + rxringp->rbr_kick_thresh = RBR_REFILL_MIN; + if (rxringp->rbr_kick_thresh < rxringp->rbr_blocks_per_page) + rxringp->rbr_kick_thresh = rxringp->rbr_blocks_per_page; + + err = sxge_rbr_fill(sxgep, rxringp, GFP_KERNEL); + if (err) + return err; + + /* txring */ + txringp->sxgep = sxgep; + err = sxge_alloc_tx_ring_info(sxgep, txringp); + if (err) + goto out_err; + } + + return 0; +out_err: + sxge_free_channels(sxgep); + return err; +} + +static int sxge_check_eps_init(struct sxge *sxgep) +{ + uint64_t niu_avail; + + niu_avail = SXGE_GET64(0xfe010); + + if (niu_avail != 0x1) { + pr_warning("%s: HW is not ready\n", sxgep->dev->name); + return -1; + } + + return 0; +} + +static int sxge_get_default_config(struct sxge *sxgep) +{ + uint64_t addr; + uint64_t rdat_low, rdat_high; + uint64_t piobar_resource_low, piobar_resource_high; + int i, j; + + /* Reading RDAT */ + addr = RDAT_LOW; + rdat_low = SXGE_GET64(addr); + + addr = RDAT_HIGH; + rdat_high = SXGE_GET64(addr); + + piobar_resource_low = rdat_low; + piobar_resource_high = rdat_high; + + for (i = 0; i < 8; i++) { + sxgep->piobar_resource[i] = (uint8_t) piobar_resource_low; + sxgep->piobar_resource[i + 8] = (uint8_t) piobar_resource_high; + piobar_resource_low = piobar_resource_low >> 8; + piobar_resource_high = piobar_resource_high >> 8; + } + + sxgep->num_rings = 0; + for (i = 0; i < MAX_VNI_NUM; i++) { + if (sxgep->piobar_resource[i]) { + uint8_t piobar_vmac_resource, vmac_cnt = 0; + uint8_t piobar_dma_resource; + + sxgep->vni = i; + piobar_dma_resource = sxgep->piobar_resource[i]; + piobar_vmac_resource = sxgep->piobar_resource[i] >> 4; + for (j = 0; j < 4; j++) { + if (piobar_dma_resource & 0x1) + sxgep->num_rings++; + + if (piobar_vmac_resource & 0x1) { + vmac_cnt++; + sxgep->intmgmt_nf = j; + } + piobar_dma_resource = piobar_dma_resource >> 1; + piobar_vmac_resource = piobar_vmac_resource >> 1; + } + if (!(sxgep->piobar_resource[i] & 0x0F) || + (vmac_cnt != 0x1)) { + SXGE_DBG("invalid piobar_resource:" + "piobar_resource[0x%x] 0x%x\n", + i, sxgep->piobar_resource[i]); + } + if (sxgep->num_rings == 0) + SXGE_DBG("invalid piobar_resource: " + "piobar_resource[0x%x] 0x%x\n", + i, sxgep->piobar_resource[i]); + } + } + + return 0; +} + +static int sxge_tx_cs_sng_poll(struct sxge *sxgep, u32 base) +{ + int limit = 1000; + + while (--limit > 0) { + u64 val = SXGE_GET64(TDC_CS(base)); + if (val & TDC_CS_SNG_STATE) + return 0; + } + return -ENODEV; +} + +static int sxge_tx_channel_stop(struct sxge *sxgep, u32 base) +{ + u64 val = SXGE_GET64(TDC_CS(base)); + + val |= TDC_CS_STOP_N_GO; + SXGE_PUT64(TDC_CS(base), val); + + return sxge_tx_cs_sng_poll(sxgep, base); +} + +static int sxge_tx_cs_reset_poll(struct sxge *sxgep, u32 base) +{ + int limit = 1000; + + while (--limit > 0) { + u64 val = SXGE_GET64(TDC_CS(base)); + if (val & TDC_CS_RST_STATE) + return 0; + } + return -ENODEV; +} + +static int sxge_tx_channel_reset(struct sxge *sxgep, u32 base) +{ + u64 val = SXGE_GET64(TDC_CS(base)); + int err; + + val |= TDC_CS_RST; + SXGE_PUT64(TDC_CS(base), val); + + err = sxge_tx_cs_reset_poll(sxgep, base); + if (!err) + SXGE_PUT64(TDC_RING_KICK(base), 0); + + return err; +} + +static int sxge_tx_channel_lpage_init(struct sxge *sxgep, u32 base) +{ + SXGE_PUT64(TDC_PG_HDL(base), 0); + + /* Need to do more? */ + + return 0; +} + +static int sxge_init_one_tx_channel(struct sxge *sxgep, + struct tx_ring_info *txringp) +{ + int err; + u32 base = txringp->tdc_base; + u64 val, ring_len; + + err = sxge_tx_channel_stop(sxgep, base); + if (err) + return err; + + err = sxge_tx_channel_reset(sxgep, base); + if (err) + return err; + + err = sxge_tx_channel_lpage_init(sxgep, base); + if (err) + return err; + + SXGE_PUT64(TXC_DMA_MAX(base), txringp->max_burst); + val = SXGE_GET64(TXC_DMA_MAX(base)); + if (val & TDC_PRSR_ENABLE) + txringp->tdc_prsr_en = 1; + + SXGE_PUT64(TDC_DMA_ENT_MSK(base), 0); + + if (txringp->descr_dma & ~(TDC_RNG_CFIG_STADDR_BASE | + TDC_RNG_CFIG_STADDR)) { + netdev_err(sxgep->dev, "TX ring channel %d " + "DMA addr (%llx) is not aligned\n", + txringp->tx_channel, + (unsigned long long) txringp->descr_dma); + return -EINVAL; + } + + /* The length field in TX_RNG_CFIG is measured in 64-byte + * blocks. rp->pending is the number of TX descriptors in + * our ring, 8 bytes each, thus we divide by 8 bytes more + * to get the proper value the chip wants. + */ + ring_len = (txringp->pending / 8); + + val = ((ring_len << TDC_RNG_CFIG_LEN_SHIFT) | + txringp->descr_dma); + SXGE_PUT64(TDC_RNG_CFIG(base), val); + + if (((txringp->mbox_dma >> 32) & ~TDC_MBH_MBADDR) || + ((u32)txringp->mbox_dma & ~TDC_MBL_MBADDR)) { + netdev_err(sxgep->dev, "TX ring channel %d " + "MBOX addr (%llx) has invalid bits\n", + txringp->tx_channel, + (unsigned long long) txringp->mbox_dma); + return -EINVAL; + } + SXGE_PUT64(TDC_MBH(base), txringp->mbox_dma >> 32); + SXGE_PUT64(TDC_MBL(base), txringp->mbox_dma & TDC_MBL_MBADDR); + + SXGE_PUT64(TDC_CS(base), 0); + + txringp->last_pkt_cnt = 0; + + return 0; +} + +static int sxge_rx_cs_sng_poll(struct sxge *sxgep, u32 base) +{ + int limit = 1000; + + while (--limit > 0) { + u64 val = SXGE_GET64(RDC_CTL_STAT(base)); + if (val & RDC_CTL_STAT_SNG_STATE) + return 0; + } + return -ENODEV; +} + +static int sxge_rx_channel_stop(struct sxge *sxgep, u32 base) +{ + u64 val = SXGE_GET64(RDC_CTL_STAT(base)); + + val |= RDC_CTL_STAT_STOP_N_GO; + SXGE_PUT64(RDC_CTL_STAT(base), val); + + return sxge_rx_cs_sng_poll(sxgep, base); +} + +static int sxge_rx_cs_reset_poll(struct sxge *sxgep, u32 base) +{ + int limit = 1000; + + while (--limit > 0) { + u64 val = SXGE_GET64(RDC_CTL_STAT(base)); + if (val & RDC_CTL_STAT_RST_STATE) + return 0; + } + return -ENODEV; +} + +static int sxge_rx_channel_reset(struct sxge *sxgep, u32 base) +{ + u64 val = SXGE_GET64(RDC_CTL_STAT(base)); + int err; + + val |= RDC_CTL_STAT_RST; + SXGE_PUT64(RDC_CTL_STAT(base), val); + + err = sxge_rx_cs_reset_poll(sxgep, base); + + return err; +} + +static int sxge_rx_channel_lpage_init(struct sxge *sxgep, u32 base) +{ + SXGE_PUT64(RDC_PAGE_HDL(base), 0); + + /* Anything else need to be done? */ + + return 0; +} + +static int sxge_compute_rdc_cfig(struct rx_ring_info *rxringp, u64 *ret) +{ + u64 val = 0; + + *ret = 0; + switch (rxringp->rbr_block_size) { + case 4 * 1024: + val |= (RBR_BLKSIZE_4K << RDC_CFG_BLKSIZE_SHIFT); + break; + case 8 * 1024: + val |= (RBR_BLKSIZE_8K << RDC_CFG_BLKSIZE_SHIFT); + break; + case 16 * 1024: + val |= (RBR_BLKSIZE_16K << RDC_CFG_BLKSIZE_SHIFT); + break; + case 32 * 1024: + val |= (RBR_BLKSIZE_32K << RDC_CFG_BLKSIZE_SHIFT); + break; + default: + return -EINVAL; + } + + val &= ~RDC_CFG_VLD2; + switch (rxringp->rbr_sizes[2]) { + case 2 * 1024: + val |= (RBR_BUFSZ2_2K << RDC_CFG_BUFSZ2_SHIFT); + break; + case 4 * 1024: + val |= (RBR_BUFSZ2_4K << RDC_CFG_BUFSZ2_SHIFT); + break; + case 8 * 1024: + val |= (RBR_BUFSZ2_8K << RDC_CFG_BUFSZ2_SHIFT); + break; + case 16 * 1024: + val |= (RBR_BUFSZ2_16K << RDC_CFG_BUFSZ2_SHIFT); + break; + + default: + return -EINVAL; + } + + val &= ~RDC_CFG_VLD1; + switch (rxringp->rbr_sizes[1]) { + case 1 * 1024: + val |= (RBR_BUFSZ1_1K << RDC_CFG_BUFSZ1_SHIFT); + break; + case 2 * 1024: + val |= (RBR_BUFSZ1_2K << RDC_CFG_BUFSZ1_SHIFT); + break; + case 4 * 1024: + val |= (RBR_BUFSZ1_4K << RDC_CFG_BUFSZ1_SHIFT); + break; + case 8 * 1024: + val |= (RBR_BUFSZ1_8K << RDC_CFG_BUFSZ1_SHIFT); + break; + + default: + return -EINVAL; + } + + val &= ~RDC_CFG_VLD0; + switch (rxringp->rbr_sizes[0]) { + case 256: + val |= (RBR_BUFSZ0_256 << RDC_CFG_BUFSZ0_SHIFT); + break; + case 512: + val |= (RBR_BUFSZ0_512 << RDC_CFG_BUFSZ0_SHIFT); + break; + case 1 * 1024: + val |= (RBR_BUFSZ0_1K << RDC_CFG_BUFSZ0_SHIFT); + break; + case 2 * 1024: + val |= (RBR_BUFSZ0_2K << RDC_CFG_BUFSZ0_SHIFT); + break; + + default: + return -EINVAL; + } + + *ret = val; + return 0; +} + +static int sxge_init_one_rx_channel(struct sxge *sxgep, + struct rx_ring_info *rxringp) +{ + int err, base = rxringp->rdc_base; + u64 val; + + err = sxge_rx_channel_stop(sxgep, base); + if (err) + return err; + + err = sxge_rx_channel_reset(sxgep, base); + if (err) + return err; + + err = sxge_rx_channel_lpage_init(sxgep, base); + if (err) + return err; + + /* Clear interrupt mask reg to enable interrupt */ + SXGE_PUT64(RDC_ENT_MSK(base), 0); + + SXGE_PUT64(RDC_MBX_CFG(base), + rxringp->mbox_dma & RDC_MBX_CFG_MBOX_STADDR); + + SXGE_PUT64(RDC_MBX_UPD_CFG(base), + (RDC_MBX_UPD_CFG_ENABLE | + ((u64)rxringp->rcr_pkt_threshold & RDC_MBX_UPD_CFG_PTHRESH))); + + SXGE_PUT64(RDC_RBR_CFG(base), + (((u64)(rxringp->rbr_table_size/8) << + RDC_RBR_CFG_LEN_SHIFT) | + (rxringp->rbr_dma & RDC_RBR_CFG_STADDR))); + + err = sxge_compute_rdc_cfig(rxringp, &val); + if (err) + return err; + SXGE_PUT64(RDC_CFG(base), val); + + SXGE_PUT64(RDC_RCR_CFG(base), + (((u64)rxringp->rcr_table_size << RDC_RCR_CFG_LEN_SHIFT) | + (rxringp->rcr_dma & RDC_RCR_CFG_STADDR))); + SXGE_PUT64(RDC_RCR_TIMER_CFG(base), + ((u64)rxringp->rcr_pkt_threshold << + RDC_RCR_TIMER_CFG_PTHRESH_SHIFT) | + RDC_RCR_TIMER_CFG_ENPTHRESH | + RDC_RCR_TIMER_CFG_ENTIMEOUT | + ((u64)rxringp->rcr_timeout << RDC_RCR_TIMER_CFG_TIMEOUT_SHIFT)); + + SXGE_PUT64(RDC_CTL_STAT(base), 0); + + rxringp->rbr_tail = (u64) rxringp->rbr_index; + SXGE_PUT64(RDC_KICK(base), + RDC_KICK_RBR_TAIL_UP_VLD | + (rxringp->rbr_tail & RDC_KICK_RBR_TAIL)); + + val = SXGE_GET64(RDC_CTL_STAT(base)); + + return 0; +} + +static void sxge_stop_one_tx_channel(struct sxge *sxgep, + struct tx_ring_info *txringp) +{ + (void) sxge_tx_channel_stop(sxgep, txringp->tdc_base); +} + +static void sxge_stop_tx_channels(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + sxge_stop_one_tx_channel(sxgep, txringp); + } +} + +static void sxge_reset_one_tx_channel(struct sxge *sxgep, + struct tx_ring_info *txringp) +{ + (void) sxge_tx_channel_reset(sxgep, txringp->tdc_base); +} + +static void sxge_reset_tx_channels(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + sxge_reset_one_tx_channel(sxgep, txringp); + } +} + +static int sxge_one_txvmac_reset(struct sxge *sxgep, u8 vni, u8 vmac) +{ + int limit; + u64 val; + + /* reset txvmac */ + val = TXVMAC_CONF_SW_RST; + SXGE_PUT64(TXVMAC_CONF(vni, vmac), val); + + limit = 1000; + while (--limit > 0) { + val = SXGE_GET64(TXVMAC_STAT(vni, vmac)); + if (val & TXVMAC_STAT_SW_RST_DONE) + break; + udelay(10); + } + if (limit <= 0) + return -ENODEV; + + return 0; +} + +static int sxge_one_txvmac_init(struct sxge *sxgep, u8 vni, u8 vmac) +{ + int err; + + err = sxge_one_txvmac_reset(sxgep, vni, vmac); + if (err) + return err; + + /* Enable interrupt bit and Clear counter */ + /* Disable interrupt for performance */ + SXGE_PUT64(TXVMAC_STAT_MSK(vni, vmac), TXVMAC_STAT_SW_RST_DONE | + TXVMAC_STAT_TX_BYTE_CNT_OVL | + TXVMAC_STAT_TX_FRAME_CNT_OVL); + + SXGE_PUT64(TXVMAC_CONF(vni, vmac), 0); + SXGE_PUT64(TXVMAC_STAT(vni, vmac), TXVMAC_STAT_SW_RST_DONE | + TXVMAC_STAT_TX_BYTE_CNT_OVL | + TXVMAC_STAT_TX_FRAME_CNT_OVL); + + return 0; +} + +static int sxge_one_rxvmac_reset(struct sxge *sxgep, u8 vni, u8 vmac) +{ + int limit; + u64 val; + + /* reset rxvmac */ + val = RXVMAC_CONFIG_RST; + SXGE_PUT64(RXVMAC_CONFIG(vni, vmac), val); + + limit = 1000; + while (--limit > 0) { + val = SXGE_GET64(RXVMAC_CONFIG(vni, vmac)); + if (val & RXVMAC_CONFIG_RST_STATE) + break; + udelay(10); + } + if (limit <= 0) + return -ENODEV; + + return 0; +} + +static void sxge_rxvmac_promisc_enable(struct sxge *sxgep, u8 vni, u8 vmac) +{ + u64 val = SXGE_GET64(RXVMAC_CONFIG(vni, vmac)); + + val |= RXVMAC_CONFIG_PROMISC_MODE; + SXGE_PUT64(RXVMAC_CONFIG(vni, vmac), val); +} + +static int sxge_one_rxvmac_enable(struct sxge *sxgep, u8 vni, u8 vmac) +{ + u64 val; + + /* Set up DMA and enable the VMAC */ + val = SXGE_GET64(RXVMAC_CONFIG(vni, vmac)); + + val |= (sxgep->piobar_resource[vni] & + SXGE_PIOBAR_RESOUCE_DMA_MASK) << + RXVMAC_CONFIG_DMA_VECTOR_SHIFT; + /* Set OP code for different configuration */ + if (sxgep->num_rings == 1) + val |= RXVMAC_CONFIG_OPCODE_1F1D; + else if (sxgep->num_rings == 4) + val |= RXVMAC_CONFIG_OPCODE_1F4D; + else if (sxgep->num_rings == 2) + val |= RXVMAC_CONFIG_OPCODE_1F2D; + + val &= ~RXVMAC_CONFIG_RST_STATE; + SXGE_PUT64(RXVMAC_CONFIG(vni, vmac), val); + + return 0; +} + +static int sxge_one_rxvmac_init(struct sxge *sxgep, u8 vni, u8 vmac) +{ + int err; + u64 val; + struct sxge_vmac_stats *vmp = &sxgep->vmac_stats; + + err = sxge_one_rxvmac_reset(sxgep, vni, vmac); + if (err) + return err; + + err = sxge_one_rxvmac_enable(sxgep, vni, vmac); + if (err) + return err; + + /* Enable interrupt bit only for link up/down */ + SXGE_PUT64(RXVMAC_INT_MASK(vni, vmac), + RXVMAC_STAT_BCAST_FRAME_CNT_OVL | + RXVMAC_STAT_MCAST_FRAME_CNT_OVL | + RXVMAC_STAT_DROP_BYTE_OVL | + RXVMAC_STAT_DROP_CNT_OVL | RXVMAC_STAT_BYTE_CNT_OVL | + RXVMAC_STAT_FRAME_CNT_OVL); + + val = SXGE_GET64(RXVMAC_STAT(sxgep->vni, sxgep->vmac)); + if (val & RXVMAC_STAT_LINK_STATE) { + vmp->rxvmac_link_state = 1; + vmp->rxvmac_link_up = 1; + vmp->rxvmac_link_down = 0; + } + + return 0; +} + +static void sxge_reset_rx_vmacs(struct sxge *sxgep) +{ + int i, j; + uint8_t piobar_resource; + + for (i = 0; i < MAX_VNI_NUM; i++) { + if (sxgep->piobar_resource[i]) { + piobar_resource = sxgep->piobar_resource[i]; + piobar_resource = piobar_resource >> + SXGE_PIOBAR_RESOUCE_VMAC_SHIFT; + for (j = 0; j < 4; j++) { + if (piobar_resource & 0x1) + sxge_one_rxvmac_reset(sxgep, i, j); + piobar_resource = piobar_resource >> 1; + } + } + } +} + +static void sxge_stop_one_rx_channel(struct sxge *sxgep, + struct rx_ring_info *rxringp) +{ + (void) sxge_rx_channel_stop(sxgep, rxringp->rdc_base); +} + +static void sxge_stop_rx_channels(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + sxge_stop_one_rx_channel(sxgep, rxringp); + } +} + +static void sxge_reset_one_rx_channel(struct sxge *sxgep, + struct rx_ring_info *rxringp) +{ + u32 base = rxringp->rdc_base; + + sxge_rx_channel_reset(sxgep, base); + SXGE_PUT64(RDC_ENT_MSK(base), RDC_ENT_MSK_ALL); +} + +static void sxge_reset_rx_channels(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + sxge_reset_one_rx_channel(sxgep, rxringp); + } +} + +static int sxge_hosteps_mbox_reset_poll(struct sxge *sxgep) +{ + int limit = 1000; + + while (--limit > 0) { + u64 val = SXGE_GET64(SXGE_MB_STAT); + if (val & SXGE_MB_STAT_FUNC_RST_DONE) + return 0; + } + return -ENODEV; +} + +static int sxge_hosteps_mbox_reset(struct sxge *sxgep) +{ + u64 val = SXGE_GET64(SXGE_MB_STAT); + int err; + + val |= SXGE_MB_STAT_FUNC_RST; + SXGE_PUT64(SXGE_MB_STAT, val); + + err = sxge_hosteps_mbox_reset_poll(sxgep); + + return err; +} + +static int sxge_init_hosteps_mbox(struct sxge *sxgep) +{ + int err; + + err = sxge_hosteps_mbox_reset(sxgep); + if (err) + return err; + + /* Disable the mbox interrupt */ + SXGE_PUT64(SXGE_MB_MSK, 0x1be); + + SXGE_PUT64(SXGE_MB_STAT, SXGE_MB_STAT_FUNC_RST_DONE); + + return 0; +} + +static int sxge_init_hw(struct sxge *sxgep) +{ + int i, j, err; + uint8_t piobar_resource; + + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, + "Initialize DMA channels\n"); + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + err = sxge_init_one_tx_channel(sxgep, txringp); + if (err) + return err; + + err = sxge_init_one_rx_channel(sxgep, rxringp); + if (err) + goto out_uninit_tx_channels; + } + + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, "Initialize VMAC\n"); + for (i = 0; i < MAX_VNI_NUM; i++) { + if (sxgep->piobar_resource[i]) { + piobar_resource = sxgep->piobar_resource[i]; + piobar_resource = piobar_resource >> + SXGE_PIOBAR_RESOUCE_VMAC_SHIFT; + for (j = 0; j < 4; j++) { + if (piobar_resource & 0x1) { + err = sxge_one_txvmac_init(sxgep, i, j); + if (err) + goto out_uninit_rx_channels; + err = sxge_one_rxvmac_init(sxgep, i, j); + if (err) + goto out_uninit_rx_channels; + } + piobar_resource = piobar_resource >> 1; + } + } + } + + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, "Initialize MBOX\n"); + err = sxge_init_hosteps_mbox(sxgep); + if (err) + goto out_uninit_rx_vmacs; + + return 0; + +out_uninit_rx_vmacs: + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, "Uninit VMAC\n"); + sxge_reset_rx_vmacs(sxgep); + +out_uninit_rx_channels: + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, "Uninit RXDMA\n"); + sxge_stop_rx_channels(sxgep); + sxge_reset_rx_channels(sxgep); + +out_uninit_tx_channels: + netif_printk(sxgep, ifup, KERN_DEBUG, sxgep->dev, "Uninit TXDMA\n"); + sxge_stop_tx_channels(sxgep); + sxge_reset_tx_channels(sxgep); + + return err; +} + +static void sxge_stop_hw(struct sxge *sxgep) +{ + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, + "Disable interrupts\n"); + sxge_enable_interrupts(sxgep, 0); + + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, "Disable VMAC\n"); + sxge_reset_rx_vmacs(sxgep); + + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, + "Stop TX channels\n"); + sxge_stop_tx_channels(sxgep); + + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, + "Stop RX channels\n"); + sxge_stop_rx_channels(sxgep); + + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, + "Reset TX channels\n"); + sxge_reset_tx_channels(sxgep); + + netif_printk(sxgep, ifdown, KERN_DEBUG, sxgep->dev, + "Reset RX channels\n"); + sxge_reset_rx_channels(sxgep); +} + +static int sxge_request_irq(struct sxge *sxgep) +{ + int i, j, err; + + for (i = 0; i < sxgep->num_ldg; i++) { + struct sxge_ldg *lp = &sxgep->ldg[i]; + + err = request_irq(lp->irq, sxge_interrupt, IRQF_SHARED, + sxgep->dev->name, lp); + if (err) + goto out_free_irqs; + } + + return 0; + +out_free_irqs: + for (j = 0; j < i; j++) { + struct sxge_ldg *lp = &sxgep->ldg[j]; + + free_irq(lp->irq, lp); + } + return err; +} + +static void sxge_free_irq(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_ldg; i++) { + struct sxge_ldg *lp = &sxgep->ldg[i]; + + free_irq(lp->irq, lp); + } +} + +static void sxge_enable_napi(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_ldg; i++) + napi_enable(&sxgep->ldg[i].napi); +} + +static void sxge_disable_napi(struct sxge *sxgep) +{ + int i; + + for (i = 0; i < sxgep->num_ldg; i++) + napi_disable(&sxgep->ldg[i].napi); +} + +static int sxge_open(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + int err; + + /* Need ? in the sxge case ? */ + netif_carrier_off(dev); + + /* need to check if EPS has done its job */ + if (sxge_check_eps_init(sxgep) < 0) { + netdev_err(dev, "%s(): sxge_check_eps_init fail\n", __func__); + return -1; + } + + /* alloc ring and buffers */ + err = sxge_alloc_channels(sxgep); + if (err) + goto out_err; + + err = sxge_enable_interrupts(sxgep, 0); + if (err) + goto out_free_channels; + + err = sxge_request_irq(sxgep); + if (err) + goto out_free_channels; + + sxge_enable_napi(sxgep); + + spin_lock_irq(&sxgep->lock); + + /* Init DMA, add interrupts */ + err = sxge_init_hw(sxgep); + if (!err) { + err = sxge_enable_interrupts(sxgep, 1); + if (err) + sxge_stop_hw(sxgep); + } + + sxgep->flags |= SXGE_FLAGS_HW_INIT; + + spin_unlock_irq(&sxgep->lock); + + if (err) { + sxge_disable_napi(sxgep); + goto out_free_irq; + } + + netif_tx_start_all_queues(dev); + + netif_carrier_on(dev); + + return 0; + +out_free_irq: + sxge_free_irq(sxgep); + +out_free_channels: + sxge_free_channels(sxgep); + +out_err: + return err; +} + +static void sxge_full_shutdown(struct sxge *sxgep, struct net_device *dev) +{ + cancel_work_sync(&sxgep->reset_task); + + sxge_disable_napi(sxgep); + netif_tx_stop_all_queues(dev); + + spin_lock_irq(&sxgep->lock); + + sxge_stop_hw(sxgep); + + spin_unlock_irq(&sxgep->lock); +} + +static int sxge_close(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + + /* TODO: shuddown HW, free irq, free channel */ + sxge_full_shutdown(sxgep, dev); + + sxge_free_irq(sxgep); + + sxge_free_channels(sxgep); + + return 0; +} + +static void sxge_vmac_stats(struct sxge *sxgep) +{ + struct sxge_vmac_stats *vmp = &sxgep->vmac_stats; + u8 vmac, vni; + + vni = sxgep->vni; + vmac = sxgep->vmac; + + vmp->txvmac_frames += SXGE_GET64(TXVMAC_FRM_CNT(vni, vmac)); + vmp->txvmac_bytes += SXGE_GET64(TXVMAC_BYT_CNT(vni, vmac)); + + vmp->rxvmac_frames += SXGE_GET64(RXVMAC_FRM_CNT(vni, vmac)); + vmp->rxvmac_bytes += SXGE_GET64(RXVMAC_BYT_CNT(vni, vmac)); + vmp->rxvmac_drops += SXGE_GET64(RXVMAC_DROP_CNT(vni, vmac)); + vmp->rxvmac_drop_bytes += + SXGE_GET64(RXVMAC_DROPBYT_CNT(vni, vmac)); + vmp->rxvmac_mcasts += SXGE_GET64(RXVMAC_MCAST_CNT(vni, vmac)); + vmp->rxvmac_bcasts += SXGE_GET64(RXVMAC_BCAST_CNT(vni, vmac)); +} + +static void sxge_get_rx_stats(struct sxge *sxgep) +{ + unsigned long pkts, dropped, errors, bytes; + struct rx_ring_info *rx_rings; + int i; + + pkts = dropped = errors = bytes = 0; + rx_rings = ACCESS_ONCE(sxgep->rx_rings); + if (!rx_rings) + goto no_rings; + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &rx_rings[i]; + + if (!rxringp) + return; + + pkts += rxringp->rx_packets; + bytes += rxringp->rx_bytes; + dropped += rxringp->rx_dropped; + errors += rxringp->rx_errors; + } + +no_rings: + sxgep->dev->stats.rx_packets = pkts; + sxgep->dev->stats.rx_bytes = bytes; + sxgep->dev->stats.rx_dropped = dropped; + sxgep->dev->stats.rx_errors = errors; +} + +static void sxge_get_tx_stats(struct sxge *sxgep) +{ + unsigned long pkts, errors, bytes; + struct tx_ring_info *tx_rings; + int i; + + pkts = errors = bytes = 0; + + tx_rings = ACCESS_ONCE(sxgep->tx_rings); + if (!tx_rings) + goto no_rings; + + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &tx_rings[i]; + + if (!txringp) + return; + + pkts += txringp->tx_packets; + bytes += txringp->tx_bytes; + errors += txringp->tx_errors; + } + +no_rings: + sxgep->dev->stats.tx_packets = pkts; + sxgep->dev->stats.tx_bytes = bytes; + sxgep->dev->stats.tx_errors = errors; +} + +static struct net_device_stats *sxge_get_stats(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + + if (netif_running(dev)) { + sxge_get_rx_stats(sxgep); + sxge_get_tx_stats(sxgep); + } + return &sxgep->dev->stats; +} + +static int sxge_eps_mbx_post(struct sxge *sxgep, u64 *req, int len) +{ + int i, limit = 10000; + u64 val = SXGE_GET64(SXGE_MB_STAT); + + if (val & SXGE_MB_STAT_OMB_FULL) + return -ENODEV; + + /* 0th entry is the last 64-bit word to be posted. */ + for (i = 1; i < len; i++) + SXGE_PUT64(SXGE_OMB(i), req[i]); + + SXGE_PUT64(SXGE_OMB(0), req[0]); + + while (--limit > 0) { + val = SXGE_GET64(SXGE_MB_STAT); + + if ((val & SXGE_MB_STAT_OMB_ACKED) || + (val & SXGE_MB_STAT_OMB_FAILED)) { + sxgep->sxge_mb_stat = val; + SXGE_PUT64(SXGE_MB_STAT, val & (SXGE_MB_STAT_OMB_ACKED | + SXGE_MB_STAT_OMB_FAILED)); + break; + } + udelay(10); + } + + if ((!limit) || (sxgep->sxge_mb_stat & SXGE_MB_STAT_OMB_FAILED)) + return -ENODEV; + + return 0; +} + +static int sxge_eps_mbx_wait_response(struct sxge *sxgep) +{ + u64 val; + int limit = 10000; + + while (--limit > 0) { + val = SXGE_GET64(SXGE_MB_STAT); + + if (val & SXGE_MB_STAT_IMB_FULL) { + SXGE_PUT64(SXGE_MB_STAT, val & SXGE_MB_STAT_IMB_FULL); + return 0; + } + udelay(10); + } + + return -ENODEV; +} + +static int sxge_eps_mbx_check_validity(struct sxge *sxgep, u64 *tag) +{ + u64 mb_tag = *tag; + u64 req = (mb_tag & MB_TAG_REQ) >> MB_TAG_REQ_SHIFT; + u64 len = (mb_tag & MB_TAG_LEN) >> MB_TAG_LEN_SHIFT; + + if (len > SXGE_MB_MAX_LEN) { + netdev_err(sxgep->dev, "%s(): len is wrong\n", __func__); + return -EINVAL; + } + + switch (req) { + case SXGE_MB_REQUEST: + case SXGE_MB_RESPONSE: + break; + default: + return -EINVAL; + } + + switch ((mb_tag & MB_TAG_TYPE) >> MB_TAG_TYPE_SHIFT) { + case SXGE_MB_GET_L2_ADDR_CAP: + case SXGE_MB_GET_TCAM_CAP: + case SXGE_MB_LINK_SPEED: + break; + case SXGE_MB_L2_ADDR_ADD: + case SXGE_MB_L2_ADDR_REM: + break; + case SXGE_MB_L2_MCAST_ADD: + case SXGE_MB_L2_MCAST_REM: + if (len != SXGE_MB_L2_ADDR_REQ_LEN) + return -EINVAL; + if (req == SXGE_MB_REQUEST) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sxge_eps_mbx_process(struct sxge *sxgep, struct sxge_mb_msg *mb_msgp) +{ + struct sxge_mb_msg mb_msg; + int i, err; + + mb_msg.msg_data[0] = SXGE_GET64(SXGE_IMB(0)); + + err = sxge_eps_mbx_check_validity(sxgep, &mb_msg.msg_data[0]); + if (err) { + SXGE_PUT64(SXGE_IMB_ACK, SXGE_IMB_ACK_IMB_NACK); + return -ENODEV; + } + + mb_msg.len = (mb_msg.msg_data[0] & MB_TAG_LEN) >> MB_TAG_LEN_SHIFT; + + for (i = 1; i < mb_msg.len; i++) + mb_msg.msg_data[i] = SXGE_GET64(SXGE_IMB(i)); + + SXGE_PUT64(SXGE_IMB_ACK, SXGE_IMB_ACK_IMB_ACK); + + memcpy(mb_msgp, &mb_msg, sizeof(struct sxge_mb_msg)); + + return 0; +} + +static int sxge_eps_mbx_mcast_add(struct sxge *sxgep, u8 *addr) +{ + struct l2_address_req l2_addr_req; + struct sxge_mb_msg mb_msg; + u64 l2_addr; + int err; + + l2_addr = ((u64) addr[0] << 40) | ((u64) addr[1] << 32) | + ((u64) addr[2] << 24) | ((u64) addr[3] << 16) | + ((u64) addr[4] << 8) | ((u64) addr[5]); + + l2_addr_req.mb_tag = ((u64) SXGE_MB_L2_ADDR_REQ_LEN | + ((u64) SXGE_MB_L2_MCAST_ADD << MB_TAG_TYPE_SHIFT) | + ((u64) SXGE_MB_REQUEST << MB_TAG_REQ_SHIFT) | + ((u64) 0xaabb << MB_TAG_SEQ_SHIFT)); + + l2_addr_req.addr = l2_addr; + l2_addr_req.mask = 0; + + err = sxge_eps_mbx_post(sxgep, (uint64_t *)&l2_addr_req, + SXGE_MB_L2_ADDR_REQ_LEN); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_post fail\n"); + return err; + } + + err = sxge_eps_mbx_wait_response(sxgep); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_wait_response fail\n"); + return err; + } + + err = sxge_eps_mbx_process(sxgep, &mb_msg); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_process fail\n"); + return err; + } + + return 0; +} + +static int sxge_eps_mbx_l2_add(struct sxge *sxgep, u8 *addr, u64 slot) +{ + struct l2_address_req l2_addr_req; + struct sxge_mb_msg mb_msg; + u64 l2_addr; + int err; + + if (slot >= (SXGE_MAX_TCAM_ENTRY_PER_FUNC * sxgep->num_rings)) { + printk("No more Alternate MAC Address Entry Available\n"); + return -EINVAL; + } + + l2_addr = ((u64) addr[0] << 40) | ((u64) addr[1] << 32) | + ((u64) addr[2] << 24) | ((u64) addr[3] << 16) | + ((u64) addr[4] << 8) | ((u64) addr[5]); + + l2_addr_req.mb_tag = (u64) SXGE_MB_L2_ADDR_REQ_LEN | + ((u64) SXGE_MB_L2_ADDR_ADD << MB_TAG_TYPE_SHIFT) | + ((u64) SXGE_MB_REQUEST << MB_TAG_REQ_SHIFT) | + ((u64) 0xaabb << MB_TAG_SEQ_SHIFT); + + l2_addr_req.addr = l2_addr; + l2_addr_req.mask = 0; + l2_addr_req.slot = slot; + + err = sxge_eps_mbx_post(sxgep, (uint64_t *)&l2_addr_req, + SXGE_MB_L2_ADDR_REQ_LEN); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_post fail\n"); + return err; + } + + err = sxge_eps_mbx_wait_response(sxgep); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_wait_response fail\n"); + return err; + } + + err = sxge_eps_mbx_process(sxgep, &mb_msg); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_process fail\n"); + return err; + } + + return 0; +} + +static int sxge_eps_mbx_link_speed(struct sxge *sxgep) +{ + struct mb_cap cap; + struct sxge_mb_msg mb_msg; + int err; + u64 pcs_mode; + + cap.mb_tag = (u64) SXGE_MB_CAP_LEN | + ((u64) SXGE_MB_LINK_SPEED << MB_TAG_TYPE_SHIFT) | + ((u64) SXGE_MB_REQUEST << MB_TAG_REQ_SHIFT) | + ((u64) 0xaabb << MB_TAG_SEQ_SHIFT); + + err = sxge_eps_mbx_post(sxgep, (uint64_t *) &cap, + SXGE_MB_CAP_LEN); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_post fail\n"); + return err; + } + + err = sxge_eps_mbx_wait_response(sxgep); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_wait_response fail\n"); + return err; + } + + err = sxge_eps_mbx_process(sxgep, &mb_msg); + if (err) { + netdev_err(sxgep->dev, "sxge_eps_mbx_process fail\n"); + return err; + } + + pcs_mode = (mb_msg.msg_data[SXGE_MB_40G_MODE_INDEX] & + SXGE_MB_PCS_MODE_MASK) >> SXGE_MB_PCS_MODE_SHIFT; + switch (pcs_mode) { + case SXGE_MB_PCS_MODE_KR4: + sxgep->link_config.speed = SPEED_40000; + break; + case SXGE_MB_PCS_MODE_X: + sxgep->link_config.speed = SPEED_1000; + break; + case SXGE_MB_PCS_MODE_KX4: + sxgep->link_config.speed = SPEED_4000; + break; + case SXGE_MB_PCS_MODE_KR: + sxgep->link_config.speed = SPEED_10000; + break; + default: + break; + } + + return 0; +} + +static void sxge_set_rx_mode(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + int alt_cnt, err; + struct netdev_hw_addr *ha; + unsigned long flags; + + spin_lock_irqsave(&sxgep->lock, flags); + sxgep->flags &= ~(SXGE_FLAGS_MCAST | SXGE_FLAGS_PROMISC); + if (dev->flags & IFF_PROMISC) { + sxgep->flags |= SXGE_FLAGS_PROMISC; + netdev_warn(dev, "Host driver can't put HW in this mode" + "Please use CLI to set it\n"); + } + if ((dev->flags & IFF_ALLMULTI) || (!netdev_mc_empty(dev))) + sxgep->flags |= SXGE_FLAGS_MCAST; + + alt_cnt = netdev_uc_count(dev); + if (alt_cnt > (SXGE_MAX_TCAM_ENTRY_PER_FUNC * sxgep->num_rings)) { + alt_cnt = SXGE_MAX_TCAM_ENTRY_PER_FUNC; + netdev_warn(dev, "Too many ucast to be set, " + "Host can only set up to 0x%x\n", + (SXGE_MAX_TCAM_ENTRY_PER_FUNC * sxgep->num_rings)); + } + + if (alt_cnt) { + /* Using slot 1 and up for alternate MAC address */ + u64 slot = 1; + + netdev_for_each_uc_addr(ha, dev) { + err = sxge_eps_mbx_l2_add(sxgep, ha->addr, slot); + if (err) + netdev_warn(dev, "Error %d adding alt mac\n", + err); + slot++; + } + } + + if (dev->flags & IFF_ALLMULTI) { + /* Set promisc bit in the RXVMAC config */ + sxge_rxvmac_promisc_enable(sxgep, sxgep->vni, sxgep->vmac); + } else if (!netdev_mc_empty(dev)) { + netdev_for_each_mc_addr(ha, dev) { + err = sxge_eps_mbx_mcast_add(sxgep, ha->addr); + if (err) + netdev_warn(dev, "Error %d ", err); + } + } + spin_unlock_irqrestore(&sxgep->lock, flags); +} + +static int sxge_set_mac_addr(struct net_device *dev, void *p) +{ + struct sxge *sxgep = netdev_priv(dev); + struct sockaddr *addr = p; + unsigned long flags; + int err; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + + if ((!netif_running(dev)) && (!(sxgep->flags & SXGE_FLAGS_HW_INIT))) + return 0; + + /* set it here ? or EPS set it */ + spin_lock_irqsave(&sxgep->lock, flags); + /* Reserve Slot 0 for Primary MAC address */ + err = sxge_eps_mbx_l2_add(sxgep, addr->sa_data, 0); + spin_unlock_irqrestore(&sxgep->lock, flags); + if (err) + return -EADDRNOTAVAIL; + + return 0; +} + +static int sxge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ +#if 0 + struct sxge *sxgep = netdev_priv(dev); + int status = 0; +#endif + switch (cmd) { + default: +#if 0 + netdev_warn(dev, "unknown ioctlcmd 0x%x\n", cmd); +#endif + break; + } + return -EOPNOTSUPP; +} + +static void sxge_netif_stop(struct sxge *sxgep) +{ + sxgep->dev->trans_start = jiffies; /* prevent tx timeout */ + + sxge_disable_napi(sxgep); + + netif_tx_disable(sxgep->dev); +} + +static void sxge_netif_start(struct sxge *sxgep) +{ + netif_tx_wake_all_queues(sxgep->dev); + + sxge_enable_napi(sxgep); + + sxge_enable_interrupts(sxgep, 1); +} + +static void sxge_reset_task(struct work_struct *work) +{ + struct sxge *sxgep = container_of(work, struct sxge, reset_task); + unsigned long flags; + int err; + + spin_lock_irqsave(&sxgep->lock, flags); + if (!netif_running(sxgep->dev)) { + spin_unlock_irqrestore(&sxgep->lock, flags); + return; + } + + spin_unlock_irqrestore(&sxgep->lock, flags); + + sxge_netif_stop(sxgep); + + spin_lock_irqsave(&sxgep->lock, flags); + + sxge_stop_hw(sxgep); + + spin_unlock_irqrestore(&sxgep->lock, flags); + + sxge_free_channels(sxgep); + + /* Re-init */ + err = sxge_get_default_config(sxgep); + if (err) + netdev_warn(sxgep->dev, + "reset sxge_get_default_config failed\n"); + + err = sxge_alloc_channels(sxgep); + if (err) { + netdev_err(sxgep->dev, "Failed to alloc sxge channels\n"); + return; + } + + spin_lock_irqsave(&sxgep->lock, flags); + + err = sxge_init_hw(sxgep); + if (!err) + sxge_netif_start(sxgep); + else + netdev_err(sxgep->dev, "Failed to init hw\n"); + + spin_unlock_irqrestore(&sxgep->lock, flags); +} + +static void sxge_tx_timeout(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + + dev_err(sxgep->device, "%s: Transmit timed out, resetting\n", + dev->name); + + schedule_work(&sxgep->reset_task); +} + +static void sxge_set_txd(struct tx_ring_info *txringp, int index, + u64 mapping, u64 len, u64 mark, u64 n_frags, + u64 ip_summed) +{ + __le64 *desc = &txringp->descr[index]; + + *desc = cpu_to_le64(mark | + (n_frags << TX_DESC_NUM_PTR_SHIFT) | + ((txringp->tdc_prsr_en & ip_summed)<< + TX_DESC_CKSUM_EN_SHIFT) | + (len << TX_DESC_TR_LEN_SHIFT) | + (mapping & TX_DESC_SAD)); +} + +static u64 sxge_compute_tx_flags(struct sk_buff *skb, struct ethhdr *ehdr, + u64 pad_bytes, u64 len) +{ + u16 eth_proto, eth_proto_inner; + u64 csum_bits, l3off, ihl, ret; + u8 ip_proto; + int ipv6; + + eth_proto = be16_to_cpu(ehdr->h_proto); + eth_proto_inner = eth_proto; + if (eth_proto == ETH_P_8021Q) { + struct vlan_ethhdr *vp = (struct vlan_ethhdr *) ehdr; + __be16 val = vp->h_vlan_encapsulated_proto; + + eth_proto_inner = be16_to_cpu(val); + } + + ipv6 = ihl = 0; + switch (skb->protocol) { + case cpu_to_be16(ETH_P_IP): + ip_proto = ip_hdr(skb)->protocol; + ihl = ip_hdr(skb)->ihl; + break; + case cpu_to_be16(ETH_P_IPV6): + ip_proto = ipv6_hdr(skb)->nexthdr; + ihl = (40 >> 2); + ipv6 = 1; + break; + default: + ip_proto = ihl = 0; + break; + } + + csum_bits = TXHDR_CSUM_NONE; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + u64 start, stuff; + + csum_bits = (ip_proto == IPPROTO_TCP ? + TXHDR_CSUM_TCP : + (ip_proto == IPPROTO_UDP ? + TXHDR_CSUM_UDP : TXHDR_CSUM_SCTP)); + + start = skb_checksum_start_offset(skb) - + (pad_bytes + sizeof(struct tx_pkt_hdr)); + stuff = start + skb->csum_offset; + + csum_bits |= (start / 2) << TXHDR_L4START_SHIFT; + csum_bits |= (stuff / 2) << TXHDR_L4STUFF_SHIFT; + } + + l3off = skb_network_offset(skb) - + (pad_bytes + sizeof(struct tx_pkt_hdr)); + + ret = (((pad_bytes / 2) << TXHDR_PAD_SHIFT) | + (len << TXHDR_LEN_SHIFT) | + ((l3off / 2) << TXHDR_L3START_SHIFT) | + (ihl << TXHDR_IHL_SHIFT) | + ((eth_proto_inner < 1536) ? TXHDR_LLC : 0) | + ((eth_proto == ETH_P_8021Q) ? TXHDR_VLAN : 0) | + (ipv6 ? TXHDR_IP_VER : 0) | + csum_bits); + + return ret; +} + +static int sxge_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + unsigned long align, headroom; + struct netdev_queue *txq; + struct tx_ring_info *txringp; + struct tx_pkt_hdr *txhdrp; + unsigned int len, nfg; + struct ethhdr *ehdr; + int prod, i, tlen; + u64 mapping, mrk; + u64 ip_summed = 0; + u32 orphan = 0; + + i = skb_get_queue_mapping(skb); + txringp = &sxgep->tx_rings[i]; + txq = netdev_get_tx_queue(dev, i); + + if (sxge_tx_avail(txringp) <= (skb_shinfo(skb)->nr_frags + 1)) { + netif_tx_stop_queue(txq); + dev_err(sxgep->device, "%s: BUG! Tx ring full when " + "queue awake!\n", dev->name); + txringp->tx_errors++; + return NETDEV_TX_BUSY; + } + + if (skb->len < ETH_ZLEN) { + unsigned int pad_bytes = ETH_ZLEN - skb->len; + + if (skb_pad(skb, pad_bytes)) + goto out; + skb_put(skb, pad_bytes); + } + + if (txringp->tdc_prsr_en != 1) { + orphan = 1; + netdev_warn(dev, "xmit txringp->tdc_prsr_en != 1\n"); + len = sizeof(struct tx_pkt_hdr) + 15; + if (skb_headroom(skb) < len) { + struct sk_buff *skb_new; + + skb_new = skb_realloc_headroom(skb, len); + if (!skb_new) { + txringp->tx_errors++; + goto out_drop; + } + kfree_skb(skb); + skb = skb_new; + } else + skb_orphan(skb); + + align = ((unsigned long) skb->data & (16 - 1)); + headroom = align + sizeof(struct tx_pkt_hdr); + + ehdr = (struct ethhdr *) skb->data; + txhdrp = (struct tx_pkt_hdr *) skb_push(skb, headroom); + + len = skb->len - sizeof(struct tx_pkt_hdr); + txhdrp->flags = cpu_to_le64( + sxge_compute_tx_flags(skb, ehdr, align, len)); + txhdrp->resv = 0; + } + + if (!orphan) + skb_orphan(skb); + + len = skb_headlen(skb); + mapping = sxgep->ops->map_single(sxgep->device, skb->data, + len, DMA_TO_DEVICE); + + if (skb->ip_summed) + ip_summed = 1; + + prod = txringp->prod; + + txringp->tx_buffs[prod].skb = skb; + txringp->tx_buffs[prod].mapping = mapping; + + mrk = TX_DESC_SOP; + if (++txringp->mark_counter == txringp->mark_freq) { + txringp->mark_counter = 0; + mrk |= TX_DESC_MARK; + txringp->mark_pending++; + } + + tlen = len; + nfg = skb_shinfo(skb)->nr_frags; + while (tlen > 0) { + tlen -= MAX_TX_DESC_LEN; + nfg++; + } + + while (len > 0) { + unsigned int this_len = len; + + if (this_len > MAX_TX_DESC_LEN) + this_len = MAX_TX_DESC_LEN; + + sxge_set_txd(txringp, prod, mapping, this_len, mrk, nfg, + ip_summed); + mrk = nfg = 0; + prod = NEXT_TX(txringp, prod); + mapping += this_len; + len -= this_len; + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + len = frag->size; + mapping = sxgep->ops->map_page(sxgep->device, frag->page, + frag->page_offset, len, + DMA_TO_DEVICE); + + txringp->tx_buffs[prod].skb = NULL; + txringp->tx_buffs[prod].mapping = mapping; + + sxge_set_txd(txringp, prod, mapping, len, 0, 0, ip_summed); + prod = NEXT_TX(txringp, prod); + } + + if (prod < txringp->prod) + txringp->wrap_bit ^= TDC_RING_KICK_WRAP; + txringp->prod = prod; + + SXGE_PUT64(TDC_RING_KICK(txringp->tdc_base), + txringp->wrap_bit | (prod << 3)); + + if (unlikely(sxge_tx_avail(txringp) <= (MAX_SKB_FRAGS + 1))) { + netif_tx_stop_queue(txq); + if (sxge_tx_avail(txringp) > SXGE_TX_WAKEUP_THRESH(txringp)) + netif_tx_wake_queue(txq); + } + +out: + return NETDEV_TX_OK; + +out_drop: + txringp->tx_errors++; + kfree_skb(skb); + goto out; +} + +static int sxge_change_mtu(struct net_device *dev, int new_mtu) +{ + int err = 0, orig_jumbo, new_jumbo; + + if ((new_mtu < 68) || (new_mtu > (SXGE_MAX_MTU - 22))) + return -EINVAL; + + orig_jumbo = (dev->mtu > ETH_DATA_LEN); + new_jumbo = (new_mtu > ETH_DATA_LEN); + + dev->mtu = new_mtu; + + if (!netif_running(dev) || + (orig_jumbo == new_jumbo)) + return 0; + + return err; +} + +static void sxge_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct sxge *sxgep = netdev_priv(dev); + + strncpy(info->driver, DRV_MODULE_NAME, 32); + strncpy(info->version, DRV_MODULE_VERSION, 32); + strncpy(info->bus_info, pci_name(sxgep->pdev), 32); +} + +static int sxge_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct sxge *sxgep = netdev_priv(dev); + struct sxge_link_config *linkp; + + if (!netif_running(dev)) + return 0; + + if (sxge_eps_mbx_link_speed(sxgep)) + netdev_warn(dev, "sxge_eps_mbx_link_speed failed\n"); + + linkp = &sxgep->link_config; + + linkp->active_speed = linkp->speed; + linkp->active_duplex = DUPLEX_FULL; + linkp->active_autoneg = 1; + linkp->active_advertising |= ADVERTISED_Autoneg; + + memset(cmd, 0, sizeof(*cmd)); + cmd->phy_address = 0; + cmd->supported = linkp->supported | SUPPORTED_Autoneg; + cmd->advertising = linkp->active_advertising; + cmd->autoneg = linkp->active_autoneg; + ethtool_cmd_speed_set(cmd, linkp->active_speed); + cmd->duplex = linkp->active_duplex; + cmd->port = PORT_FIBRE; + + return 0; +} + +static int sxge_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + return -EINVAL; +} + +static u32 sxge_get_msglevel(struct net_device *dev) +{ + struct sxge *sxgep = netdev_priv(dev); + return sxgep->msg_enable; +} + +static void sxge_set_msglevel(struct net_device *dev, u32 value) +{ + struct sxge *sxgep = netdev_priv(dev); + sxgep->msg_enable = value; +} + +static const struct { + const char string[ETH_GSTRING_LEN]; +} sxge_vmac_stat_keys[] = { + { "tx_frames" }, + { "tx_bytes" }, + { "tx_frame_cnt_ovl" }, + { "tx_byte_cnt_ovl" }, + { "rx_frames" }, + { "rx_bytes" }, + { "rx_drops" }, + { "rx_drop_bytes" }, + { "rx_mcasts" }, + { "rx_bcasts" }, + { "rx_frames_cnt_ovl" }, + { "rx_byte_cnt_ovl" }, + { "rx_drop_byte_ovl" }, + { "rx_drop_cnt_ovl" }, + { "rx_mcast_frame_cnt_ovl" }, + { "rx_bcast_frame_cnt_ovl" }, + { "rx_link_up" }, + { "rx_link_down" }, + { "rx_link_state" }, +}; + +#define NUM_VMAC_STAT_KEYS ARRAY_SIZE(sxge_vmac_stat_keys) + +static const struct { + const char string[ETH_GSTRING_LEN]; +} sxge_rxchan_stat_keys[] = { + { "rx_channel" }, + { "rx_packets" }, + { "rx_bytes" }, + { "rx_dropped" }, + { "rx_errors" }, + { "rx_hw_pktcnt" }, + { "rx_hw_pktdrop" }, + { "rx_rbr_empty" }, + { "rx_fifo_error" }, + { "rx_rcr_shadow_full" }, +}; + +#define NUM_RXCHAN_STAT_KEYS ARRAY_SIZE(sxge_rxchan_stat_keys) + +static const struct { + const char string[ETH_GSTRING_LEN]; +} sxge_txchan_stat_keys[] = { + { "tx_channel" }, + { "tx_packets" }, + { "tx_bytes" }, + { "tx_errors" }, +}; + +#define NUM_TXCHAN_STAT_KEYS ARRAY_SIZE(sxge_txchan_stat_keys) + +static void sxge_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + struct sxge *sxgep = netdev_priv(dev); + int i; + + if (stringset != ETH_SS_STATS) + return; + + memcpy(data, sxge_vmac_stat_keys, + sizeof(sxge_vmac_stat_keys)); + data += sizeof(sxge_vmac_stat_keys); + + for (i = 0; i < sxgep->num_rings; i++) { + memcpy(data, sxge_rxchan_stat_keys, + sizeof(sxge_rxchan_stat_keys)); + data += sizeof(sxge_rxchan_stat_keys); + } + for (i = 0; i < sxgep->num_rings; i++) { + memcpy(data, sxge_txchan_stat_keys, + sizeof(sxge_txchan_stat_keys)); + data += sizeof(sxge_txchan_stat_keys); + } +} + +static int sxge_get_sset_count(struct net_device *dev, int stringset) +{ + struct sxge *sxgep = netdev_priv(dev); + + if (stringset != ETH_SS_STATS) + return -EINVAL; + + return NUM_VMAC_STAT_KEYS + + (sxgep->num_rings * NUM_RXCHAN_STAT_KEYS) + + (sxgep->num_rings * NUM_TXCHAN_STAT_KEYS); +} + +static void sxge_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct sxge *sxgep = netdev_priv(dev); + int i; + + sxge_vmac_stats(sxgep); + + memcpy(data, &sxgep->vmac_stats, + sizeof(struct sxge_vmac_stats)); + data += (sizeof(struct sxge_vmac_stats) / sizeof(u64)); + + for (i = 0; i < sxgep->num_rings; i++) { + struct rx_ring_info *rxringp = &sxgep->rx_rings[i]; + + if (!rxringp) + return; + + rxringp->rx_hw_pktcnt += + SXGE_GET64(RDC_PKTCNT(rxringp->rdc_base)) & + RDC_PKTCNT_COUNT; + rxringp->rx_hw_pktdrop += + SXGE_GET64(RDC_PKTDROP(rxringp->rdc_base)) & + RDC_PKTDROP_COUNT; + + data[0] = (u64) rxringp->rx_channel; + data[1] = rxringp->rx_packets; + data[2] = rxringp->rx_bytes; + data[3] = rxringp->rx_dropped; + data[4] = rxringp->rx_errors; + data[5] = rxringp->rx_hw_pktcnt; + data[6] = rxringp->rx_hw_pktdrop; + data[7] = rxringp->rx_rbr_empty; + data[8] = rxringp->rx_fifo_error; + data[9] = rxringp->rx_rcr_shadow_full; + data += 10; + } + for (i = 0; i < sxgep->num_rings; i++) { + struct tx_ring_info *txringp = &sxgep->tx_rings[i]; + + if (!txringp) + return; + + data[0] = (u64) txringp->tx_channel; + data[1] = txringp->tx_packets; + data[2] = txringp->tx_bytes; + data[3] = txringp->tx_errors; + data += 4; + } +} + +static const struct ethtool_ops sxge_ethtool_ops = { + .get_drvinfo = sxge_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = sxge_get_msglevel, + .set_msglevel = sxge_set_msglevel, + .get_settings = sxge_get_settings, + .set_settings = sxge_set_settings, + .get_strings = sxge_get_strings, + .get_sset_count = sxge_get_sset_count, + .get_ethtool_stats = sxge_get_ethtool_stats, +}; + +static int sxge_ldg_assign_ldn(struct sxge *sxgep, int ldg, int ldn) +{ + u8 vni = sxgep->vni, func = sxgep->intmgmt_nf; + u64 val; + + if (ldg < SXGE_LDG_MIN || ldg > SXGE_LDG_MAX) + return -EINVAL; + if (ldn < 0 || ldn > LDN_MAX) + return -EINVAL; + + sxgep->ldg_map[ldn] = ldg; + + val = (ldg & LD_MSK_GNUM_LDG_NUM) | LD_MSK_GNUM_EN_LDG_WR; + SXGE_PUT64(LD_MSK_GNUM(vni, func, ldn), val); + + return 0; +} + +static void __devinit sxge_try_msix(struct sxge *sxgep, u8 *ldg_num_map) +{ + struct msix_entry msi_vec[SXGE_NUM_LDG]; + struct pci_dev *pdev = sxgep->pdev; + int i, num_irqs, err; + + for (i = 0; i < SXGE_NUM_LDG; i++) + ldg_num_map[i] = i; + + num_irqs = (sxgep->num_rings * 2) + 5; +retry: + for (i = 0; i < num_irqs; i++) { + msi_vec[i].vector = 0; + msi_vec[i].entry = i; + } + + err = pci_enable_msix(pdev, msi_vec, num_irqs); + if (err < 0) { + sxgep->flags &= ~SXGE_FLAGS_MSIX; + return; + } + if (err > 0) { + num_irqs = err; + goto retry; + } + + sxgep->flags |= SXGE_FLAGS_MSIX; + for (i = 0; i < num_irqs; i++) + sxgep->ldg[i].irq = msi_vec[i].vector; + sxgep->num_ldg = num_irqs; +} + +static int __devinit sxge_ldg_init(struct sxge *sxgep) +{ + u8 ldg_num_map[SXGE_NUM_LDG]; + int i, err, ldg_rotor; + + sxgep->num_ldg = 1; + sxgep->ldg[0].irq = sxgep->dev->irq; + + sxge_try_msix(sxgep, ldg_num_map); + + for (i = 0; i < sxgep->num_ldg; i++) { + struct sxge_ldg *lp = &sxgep->ldg[i]; + + netif_napi_add(sxgep->dev, &lp->napi, sxge_poll, 64); + + lp->sxgep = sxgep; + lp->ldg_num = ldg_num_map[i]; + lp->timer = 2; /* XXX */ + + /* SID ? */ + } + + /* + * LDG assignment ordering + * + * RX DMA channels + * TX DMA channels + * Mailbox + * RX VMAC + * TX VMAC + * VNI error + */ + ldg_rotor = 0; + + for (i = 0; i < sxgep->num_rings; i++) { + err = sxge_ldg_assign_ldn(sxgep, + ldg_num_map[ldg_rotor], LDN_RXDMA(i)); + if (err) + return err; + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + } + + for (i = 0; i < sxgep->num_rings; i++) { + err = sxge_ldg_assign_ldn(sxgep, + ldg_num_map[ldg_rotor], LDN_TXDMA(i)); + if (err) + return err; + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + } + + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + err = sxge_ldg_assign_ldn(sxgep, ldg_num_map[ldg_rotor], LDN_MAILBOX); + if (err) + return err; + + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + err = sxge_ldg_assign_ldn(sxgep, ldg_num_map[ldg_rotor], LDN_RXVMAC); + if (err) + return err; + + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + err = sxge_ldg_assign_ldn(sxgep, ldg_num_map[ldg_rotor], LDN_TXVMAC); + if (err) + return err; + + ldg_rotor++; + if (ldg_rotor == sxgep->num_ldg) + ldg_rotor = 0; + err = sxge_ldg_assign_ldn(sxgep, ldg_num_map[ldg_rotor], LDN_VNI_ERROR); + if (err) + return err; + + return 0; +} + +static void __devexit sxge_ldg_free(struct sxge *sxgep) +{ + if (sxgep->flags & SXGE_FLAGS_MSIX) + pci_disable_msix(sxgep->pdev); +} + +static int __devinit sxge_get_invariants(struct sxge *sxgep) +{ + int i, err = 0; + struct net_device *dev = sxgep->dev; + unsigned char mac_addr[6]; + u64 val; + + if (sxge_get_default_config(sxgep) < 0) { + netdev_warn(sxgep->dev, "sxge_get_default_config fail\n"); + return -1; + } + + val = SXGE_GET64(0xf00b8); + mac_addr[5] = (u8)(val & 0xff); + mac_addr[4] = (u8)((val >> 8) & 0xff); + mac_addr[3] = (u8)((val >> 16) & 0xff); + mac_addr[2] = (u8)((val >> 24) & 0xff); + mac_addr[1] = (u8)((val >> 32) & 0xff); + mac_addr[0] = (u8)((val >> 40) & 0xff); + + memcpy(dev->perm_addr, mac_addr, ETH_ALEN); + memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len); + + /* irq disable */ + for (i = 0; i <= LDN_MAX; i++) + sxge_ldn_irq_enable(sxgep, i, 0); + + /* LDG init for interrupt */ + sxge_ldg_init(sxgep); + + return err; +} + +static void *sxge_pci_alloc_coherent(struct device *dev, size_t size, + u64 *handle, gfp_t flag) +{ + dma_addr_t dh; + void *ret; + + ret = dma_alloc_coherent(dev, size, &dh, flag); + if (ret) + *handle = dh; + return ret; +} + +static void sxge_pci_free_coherent(struct device *dev, size_t size, + void *cpu_addr, u64 handle) +{ + dma_free_coherent(dev, size, cpu_addr, handle); +} + +static u64 sxge_pci_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction direction) +{ + return dma_map_page(dev, page, offset, size, direction); +} + +static void sxge_pci_unmap_page(struct device *dev, u64 dma_address, + size_t size, enum dma_data_direction direction) +{ + dma_unmap_page(dev, dma_address, size, direction); +} + +static u64 sxge_pci_map_single(struct device *dev, void *cpu_addr, + size_t size, enum dma_data_direction direction) +{ + return dma_map_single(dev, cpu_addr, size, direction); +} + +static void sxge_pci_unmap_single(struct device *dev, u64 dma_address, + size_t size, enum dma_data_direction direction) +{ + dma_unmap_single(dev, dma_address, size, direction); +} + +static const struct sxge_ops sxge_pci_ops = { + .alloc_coherent = sxge_pci_alloc_coherent, + .free_coherent = sxge_pci_free_coherent, + .map_page = sxge_pci_map_page, + .unmap_page = sxge_pci_unmap_page, + .map_single = sxge_pci_map_single, + .unmap_single = sxge_pci_unmap_single, +}; + +static void __devinit sxge_driver_version(void) +{ + static int sxge_version_printed; + + if (sxge_version_printed++ == 0) + pr_info("%s", version); +} + +static struct net_device * __devinit sxge_alloc_and_init( + struct device *gen_dev, struct pci_dev *pdev, + const struct sxge_ops *ops, u8 devfn) +{ + struct net_device *dev; + struct sxge *sxgep; + + dev = alloc_etherdev_mq(sizeof(struct sxge), 4); + if (!dev) { + dev_err(gen_dev, "Etherdev alloc failed, aborting\n"); + return NULL; + } + + SET_NETDEV_DEV(dev, gen_dev); + + sxgep = netdev_priv(dev); + sxgep->dev = dev; + sxgep->pdev = pdev; + sxgep->device = gen_dev; + sxgep->ops = ops; + + sxgep->msg_enable = sxge_debug; + + spin_lock_init(&sxgep->lock); + INIT_WORK(&sxgep->reset_task, sxge_reset_task); + + sxgep->devfn = devfn; + + return dev; +} + +static const struct net_device_ops sxge_netdev_ops = { + .ndo_open = sxge_open, + .ndo_stop = sxge_close, + .ndo_start_xmit = sxge_start_xmit, + .ndo_get_stats = sxge_get_stats, + .ndo_set_multicast_list = sxge_set_rx_mode, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = sxge_set_mac_addr, + .ndo_do_ioctl = sxge_ioctl, + .ndo_tx_timeout = sxge_tx_timeout, + .ndo_change_mtu = sxge_change_mtu, +}; + +static void __devinit sxge_assign_netdev_ops(struct net_device *dev) +{ + dev->netdev_ops = &sxge_netdev_ops; + dev->ethtool_ops = &sxge_ethtool_ops; + dev->watchdog_timeo = SXGE_TX_TIMEOUT; +} + +static void __devinit sxge_device_announce(struct sxge *sxgep) +{ + struct net_device *dev = sxgep->dev; + + pr_info("%s: SXGE Ethernet %pM\n", dev->name, dev->dev_addr); +} + +static int __devinit sxge_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct sxge *sxgep; + int err, pos; + u64 dma_mask; + u16 val16; + + sxge_driver_version(); + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); + return err; + } + + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || + !(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "Cannot find proper PCI device " + "base addresses, aborting\n"); + err = -ENODEV; + goto err_out_disable_pdev; + } + + err = pci_request_regions(pdev, DRV_MODULE_NAME); + if (err) { + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); + goto err_out_disable_pdev; + } + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (pos <= 0) { + dev_err(&pdev->dev, "Cannot find PCI Express capability, " + "aborting\n"); + goto err_out_free_res; + } + + dev = sxge_alloc_and_init(&pdev->dev, pdev, + &sxge_pci_ops, PCI_FUNC(pdev->devfn)); + + if (!dev) { + err = -ENOMEM; + goto err_out_free_res; + } + + sxgep = netdev_priv(dev); + + /* Need to get pci.domain, pci_bus, pci_device? */ + sxgep->dev_busnum = pdev->bus->number; + + pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16); + val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN; + val16 |= (PCI_EXP_DEVCTL_CERE | + PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_URRE | + PCI_EXP_DEVCTL_RELAX_EN); + pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16); + + dma_mask = DMA_BIT_MASK(44); + err = pci_set_dma_mask(pdev, dma_mask); + if (!err) { + dev->features |= NETIF_F_HIGHDMA; + err = pci_set_consistent_dma_mask(pdev, dma_mask); + if (err) { + dev_err(&pdev->dev, "Unable to obtain 44 bit " + "DMA for consistent allocations, " + "aborting\n"); + goto err_out_free_dev; + } + } + + if (err || dma_mask == DMA_BIT_MASK(32)) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "No usable DMA configuration, " + "aborting\n"); + goto err_out_free_dev; + } + } +#if 0 + /* + * Network stack pass down the pkts with checksum + * If driver enable the HW checksum feature, + * the checksum value ends up wrong + */ + dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM); +#endif + + sxgep->regs = pci_ioremap_bar(pdev, 2); + if (!sxgep->regs) { + dev_err(&pdev->dev, "Cannot map device registers, " + "aborting\n"); + err = -ENOMEM; + goto err_out_free_dev; + } + + pci_set_master(pdev); + pci_save_state(pdev); + + /* Assign dev ops */ + dev->irq = pdev->irq; + sxge_assign_netdev_ops(dev); + + /* LDG INIT for interrupts */ + err = sxge_get_invariants(sxgep); + if (err) { + if (err != -ENODEV) + dev_err(&pdev->dev, "Problem fetching invariants " + "of chip, aborting\n"); + goto err_out_iounmap; + } + +#ifdef CONFIG_PCI_IOV + if (pdev->device == 0x207a) { + struct pci_sriov *iov; + u16 vfs; + + iov = sxgep->pdev->sriov; + if (!iov) + netdev_warn(dev, "iov is NULL, not a SR-IOV device\n"); + + if (!sxgep->pdev->is_physfn) + netdev_warn(dev, "No PF is seen, !dev->is_physfn\n"); + + pci_read_config_word(pdev, 0x16c, &vfs); + err = pci_enable_sriov(pdev, vfs); + if (err) { + netdev_warn(dev, "misconfiguration, " + "pci_enable_sriov failed\n"); + } else { + sxgep->flags |= SXGE_FLAGS_SRIOV; + } + } +#endif + + /* The following will be done in sxge_get_invariants and and sxge_open*/ + /* Init code PFC? */ + /* Not sxge */ + /* TODO: INIT param */ + /* TODO: INIT STATS */ + /* TODO: INIT DMA pages */ + /* TODO: INIT real HW */ + /* TODO: INIT XCVR */ + + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "Cannot register net device, " + "aborting\n"); + goto err_out_iounmap; + } + + pci_set_drvdata(pdev, dev); + + sxge_device_announce(sxgep); + + return 0; + +err_out_iounmap: + if (sxgep->regs) { + iounmap(sxgep->regs); + sxgep->regs = NULL; + } + +err_out_free_dev: + free_netdev(dev); + +err_out_free_res: + pci_release_regions(pdev); + +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + return err; +} + +static void __devexit sxge_pci_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + struct sxge *sxgep = netdev_priv(dev); + + sxgep->flags &= ~SXGE_FLAGS_HW_INIT; + + unregister_netdev(dev); + + if (pdev->device == 0x207a) { + if (sxgep->flags & SXGE_FLAGS_SRIOV) { +#ifdef CONFIG_PCI_IOV + pci_disable_sriov(pdev); + sxgep->flags &= ~SXGE_FLAGS_SRIOV; +#endif + } + } + + if (sxgep->regs) { + iounmap(sxgep->regs); + sxgep->regs = NULL; + } + + sxge_ldg_free(sxgep); + + free_netdev(dev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + } +} + +static int sxge_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sxge *sxgep = netdev_priv(dev); + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + flush_work_sync(&sxgep->reset_task); + sxge_netif_stop(sxgep); + + /* TODO: DISABLE & STOP HW */ + spin_lock_irqsave(&sxgep->lock, flags); + sxge_enable_interrupts(sxgep, 0); + spin_unlock_irqrestore(&sxgep->lock, flags); + + netif_device_detach(dev); + + spin_lock_irqsave(&sxgep->lock, flags); + sxge_stop_hw(sxgep); + spin_unlock_irqrestore(&sxgep->lock, flags); + + pci_save_state(pdev); + + return 0; +} + +static int sxge_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sxge *sxgep = netdev_priv(dev); + unsigned long flags; + int err; + + if (!netif_running(dev)) + return 0; + + pci_restore_state(pdev); + + netif_device_attach(dev); + + spin_lock_irqsave(&sxgep->lock, flags); + + /* TODO: Init & start HW */ + err = sxge_init_hw(sxgep); + if (!err) + sxge_netif_start(sxgep); + + spin_unlock_irqrestore(&sxgep->lock, flags); + + return err; +} + +static struct pci_driver sxge_pci_driver = { + .name = DRV_MODULE_NAME, + .id_table = sxge_pci_tbl, + .probe = sxge_pci_init_one, + .remove = __devexit_p(sxge_pci_remove_one), + .suspend = sxge_suspend, + .resume = sxge_resume, +}; + +static int __init sxge_init(void) +{ + int err = 0; + + sxge_debug = netif_msg_init(debug, SXGE_MSG_DEFAULT); + + if (!err) + err = pci_register_driver(&sxge_pci_driver); + + return err; +} + +static void __exit sxge_exit(void) +{ + pci_unregister_driver(&sxge_pci_driver); +} + +module_init(sxge_init); +module_exit(sxge_exit); diff --git a/drivers/net/sxge/sxge.h b/drivers/net/sxge/sxge.h new file mode 100644 index 0000000000000..e9f04549f9d57 --- /dev/null +++ b/drivers/net/sxge/sxge.h @@ -0,0 +1,705 @@ +/* sxge.h: Definitions for SOL ethernet driver. + * Copyright (C) 2011 Oracle Corp + */ +/* #pragma ident "@(#)sxge.h 1.21 11/02/10 SMI" */ + +#ifndef _SXGE_H +#define _SXGE_H + +#define SXGE_MAX_MTU 9216 +#define RDAT_LOW 0xFE000 +#define RDAT_HIGH 0xFE008 +#define MAX_PIOBAR_RESOURCE 0x10 +#define MAX_VNI_NUM 0xC +#define SXGE_PIOBAR_RESOUCE_VMAC_SHIFT 4 +#define SXGE_PIOBAR_RESOUCE_DMA_MASK 0xF +/* + * DMA HW Interfaces + */ +#define HOST_VNI_BASE (0x00000) +#define TXDMA_BASE (0x00000) +#define RXDMA_BASE (0x00400) +#define RXVMAC_BASE (0x08000) +#define TXVMAC_BASE (0x08200) +#define INTR_BASE (0x08400) +#define SHARE_RESOURCE_BASE (0xC0000) +#define STAND_RESOURCE_BASE (0xF0000) + +#define VNI_STEP (0x10000) +#define RXDMA_STEP (0x02000) +#define TXDMA_STEP (0x02000) +#define VMAC_STEP (0x02000) +#define NF_STEP (0x02000) + +/* LDG */ +#define LD_BASE(VNI, NF, ID) (VNI * VNI_STEP + NF * NF_STEP + ID * 8ULL) + +#define LD_MSK_GNUM(VNI, NF, LDN) (LD_BASE(VNI, NF, LDN) + 0x8400) +#define LD_MSK_GNUM_LDG_NUM 0x000000000000000fULL +#define LD_MSK_GNUM_LDG_MSK 0x0000000000000030ULL +#define LD_MSK_GNUM_LDG_MSK_SHIFT 4 +#define LD_MSK_GNUM_EN_LDG_WR 0x0000000000000040ULL +#define LD_MSK_GNUM_EN_MSK_WR 0x0000000000000080ULL + +#define LDSV(VNI, NF, LDG) (LD_BASE(VNI, NF, LDG) + 0x8480) +#define LDSV_LDSV1_MASK 0x000000001fff0000ULL +#define LDSV_V1_VNI_ERROR 0x0000000010000000ULL +#define LDSV_V1_TXVMAC 0x0000000008000000ULL +#define LDSV_V1_RXVMAC 0x0000000004000000ULL +#define LDSV_V1_MAILBOX 0x0000000002000000ULL +#define LDSV_V1_TXDMA 0x0000000000f00000ULL +#define LDSV_V1_RXDMA 0x00000000000f0000ULL +#define LDSV_LDSV0_MASK 0x0000000000001fffULL +#define LDSV_V0_VNI_ERROR 0x0000000000001000ULL +#define LDSV_V0_TXVMAC 0x0000000000000800ULL +#define LDSV_V0_RXVMAC 0x0000000000000400ULL +#define LDSV_V0_MAILBOX 0x0000000000000200ULL +#define LDSV_V0_TXDMA 0x00000000000000f0ULL +#define LDSV_V0_RXDMA 0x000000000000000fULL + +#define LDG_IMGMT(LDG) (STAND_RESOURCE_BASE + LDG * 8) +#define LDG_IMGMT_TIMER 0x00000000003f0000ULL +#define LDG_IMGMT_TIMER_SHIFT 16 +#define LDG_IMGMT_ARM 0x0000000000001000ULL +#define LDG_IMGMT_VNI_STAT 0x0000000000000FFFULL + +/* DMA */ +#define RDC_BASE(vni, rdc) ((vni * VNI_STEP) + (rdc * RXDMA_STEP)) +#define TDC_BASE(vni, tdc) ((vni * VNI_STEP) + (tdc * TXDMA_STEP)) +#define VMAC_BASE(vni, vmac) ((vni * VNI_STEP) + (vmac * VMAC_STEP)) + +/* VMAC registers */ +#define TXVMAC_CONF(VNI, VMAC) (TXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0) +#define TXVMAC_CONF_SW_RST 0x0000000000000001ULL + +#define TXVMAC_STAT(VNI, VMAC) (TXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x8) +#define TXVMAC_STAT_TX_BYTE_CNT_OVL 0x0000000000000004ULL +#define TXVMAC_STAT_TX_FRAME_CNT_OVL 0x0000000000000002ULL +#define TXVMAC_STAT_SW_RST_DONE 0x0000000000000001ULL + +#define TXVMAC_STAT_MSK(VNI, VMAC) (TXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x10) + +#define TXVMAC_FRM_CNT(VNI, VMAC) (TXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x20) +#define TXVMAC_BYT_CNT(VNI, VMAC) (TXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x28) + +#define RXVMAC_CONFIG(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0) +#define RXVMAC_CONFIG_OPCODE 0x0000000000003000ULL +#define RXVMAC_CONFIG_OPCODE_1F4D 0x0000000000003000ULL +#define RXVMAC_CONFIG_OPCODE_1F1D 0x0000000000000000ULL +#define RXVMAC_CONFIG_OPCODE_1F2D 0x0000000000001000ULL +#define RXVMAC_CONFIG_OPCODE_SHIFT 12 +#define RXVMAC_CONFIG_DMA_VECTOR 0x0000000000000F00ULL +#define RXVMAC_CONFIG_DMA_VECTOR_SHIFT 8 +#define RXVMAC_CONFIG_PROMISC_MODE 0x0000000000000080ULL +#define RXVMAC_CONFIG_RST 0x0000000000000008ULL +#define RXVMAC_CONFIG_RST_STATE 0x0000000000000004ULL + +#define RXVMAC_STAT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x40) +#define RXVMAC_STAT_LINK_STATE 0x0000000000008000ULL +#define RXVMAC_STAT_LINK_DOWN 0x0000000000000100ULL +#define RXVMAC_STAT_LINK_UP 0x0000000000000080ULL +#define RXVMAC_STAT_BCAST_FRAME_CNT_OVL 0x0000000000000040ULL +#define RXVMAC_STAT_MCAST_FRAME_CNT_OVL 0x0000000000000020ULL +#define RXVMAC_STAT_DROP_BYTE_OVL 0x0000000000000010ULL +#define RXVMAC_STAT_DROP_CNT_OVL 0x0000000000000008ULL +#define RXVMAC_STAT_BYTE_CNT_OVL 0x0000000000000004ULL +#define RXVMAC_STAT_FRAME_CNT_OVL 0x0000000000000002ULL + +#define RXVMAC_INT_MASK(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x48) +#define RXVMAC_FRMCNT_DBG(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x10) +#define RXVMAC_FRM_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x60) +#define RXVMAC_BYT_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x68) +#define RXVMAC_DROP_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x70) +#define RXVMAC_DROPBYT_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC)+0x78) +#define RXVMAC_MCAST_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x80) +#define RXVMAC_BCAST_CNT(VNI, VMAC) (RXVMAC_BASE + VMAC_BASE(VNI, VMAC) + 0x88) + +/* TX DMA register */ +#define TDC_RNG_CFIG(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0000) +#define TDC_RNG_CFIG_LEN 0x1fff000000000000ULL +#define TDC_RNG_CFIG_LEN_SHIFT 48 +#define TDC_RNG_CFIG_STADDR_BASE 0x00000ffffff80000ULL +#define TDC_RNG_CFIG_STADDR 0x000000000007ffc0ULL + +#define TDC_PG_HDL(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0008) +#define TDC_RING_HDL(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0010) +#define TDC_RING_KICK(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0018) +#define TDC_RING_KICK_WRAP 0x0000000000080000ULL +#define TDC_RING_KICK_TAIL 0x000000000007fff8ULL + +#define TDC_DMA_ENT_MSK(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0020) + +#define TDC_CS(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0028) +#define TDC_CS_PKT_CNT 0x0fff000000000000ULL +#define TDC_CS_PKT_CNT_SHIFT 48 +#define TDC_CS_RST 0x0000000080000000ULL +#define TDC_CS_RST_STATE 0x0000000040000000ULL +#define TDC_CS_STOP_N_GO 0x0000000010000000ULL +#define TDC_CS_SNG_STATE 0x0000000008000000ULL +#define TDC_CS_MK 0x0000000000008000ULL +#define TDC_CS_MMK 0x0000000000004000ULL +#define TDC_CS_REJECT_RESP_ERR 0x0000000000001000ULL +#define TDC_CS_SOP_BIT_ERR 0x0000000000000800ULL +#define TDC_CS_PREMATURE_SOP_ERR 0x0000000000000400ULL +#define TDC_CS_DESC_LENGTH_ERR 0x0000000000000200ULL +#define TDC_CS_DESC_NUM_PTR_ERR 0x0000000000000100ULL +#define TDC_CS_MBOX_ERR 0x0000000000000080ULL +#define TDC_CS_PKT_SIZE_ERR 0x0000000000000040ULL +#define TDC_CS_TX_RING_OFLOW 0x0000000000000020ULL +#define TDC_CS_PREF_BUF_PAR_ERR 0x0000000000000010ULL +#define TDC_CS_NACK_PREF 0x0000000000000008ULL +#define TDC_CS_NACK_PKT_RD 0x0000000000000004ULL +#define TDC_CS_CONF_PART_ERR 0x0000000000000002ULL +#define TDC_CS_PKT_PRT_ERR 0x0000000000000001ULL + +#define TDC_MBH(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0030) +#define TDC_MBH_MBADDR 0x0000000000000fffULL + +#define TDC_MBL(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0038) +#define TDC_MBL_MBADDR 0x00000000ffffffc0ULL + +#define TDC_RNG_ERR_LOGH(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0048) +#define TDC_RNG_ERR_LOGL(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x00508) + +#define TXC_DMA_MAX(CHAN_BASE) (TXDMA_BASE + CHAN_BASE + 0x0200) +#define TDC_PRSR_ENABLE 0x0000000080000000ULL + +/* rx dma */ +#define RDC_PAGE_HDL(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0000) + +#define RDC_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0008) +#define RDC_CFG_BLKSIZE 0x0000000000000600ULL +#define RDC_CFG_BLKSIZE_SHIFT 9 +#define RDC_CFG_BUFSZ2 0x0000000000000180ULL +#define RDC_CFG_BUFSZ2_SHIFT 7 +#define RDC_CFG_VLD2 0x0000000000000040ULL +#define RDC_CFG_BUFSZ1 0x0000000000000030ULL +#define RDC_CFG_BUFSZ1_SHIFT 4 +#define RDC_CFG_VLD1 0x0000000000000008ULL +#define RDC_CFG_BUFSZ0 0x0000000000000006ULL +#define RDC_CFG_BUFSZ0_SHIFT 1 +#define RDC_CFG_VLD0 0x0000000000000001ULL + +#define RBR_BLKSIZE_4K 0x0 +#define RBR_BLKSIZE_8K 0x1 +#define RBR_BLKSIZE_16K 0x2 +#define RBR_BLKSIZE_32K 0x3 +#define RBR_BUFSZ2_2K 0x0 +#define RBR_BUFSZ2_4K 0x1 +#define RBR_BUFSZ2_8K 0x2 +#define RBR_BUFSZ2_16K 0x3 +#define RBR_BUFSZ1_1K 0x0 +#define RBR_BUFSZ1_2K 0x1 +#define RBR_BUFSZ1_4K 0x2 +#define RBR_BUFSZ1_8K 0x3 +#define RBR_BUFSZ0_256 0x0 +#define RBR_BUFSZ0_512 0x1 +#define RBR_BUFSZ0_1K 0x2 +#define RBR_BUFSZ0_2K 0x3 + +#define RDC_RBR_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0010) +#define RDC_RBR_CFG_LEN 0x01ff000000000000ULL +#define RDC_RBR_CFG_LEN_SHIFT 48 +#define RDC_RBR_CFG_STADDR 0x00000fffffffffc0ULL + +#define RDC_RCR_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0018) +#define RDC_RCR_CFG_LEN 0xffff000000000000ULL +#define RDC_RCR_CFG_LEN_SHIFT 48 +#define RDC_RCR_CFG_STADDR 0x00000fffffffffc0ULL + +#define RDC_MBX_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0020) +#define RDC_MBX_CFG_MBOX_STADDR 0x00000fffffffffc0ULL + +#define RDC_RCR_TIMER_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0028) +#define RDC_RCR_TIMER_CFG_PTHRESH 0x00000000ffff0000ULL +#define RDC_RCR_TIMER_CFG_PTHRESH_SHIFT 16 +#define RDC_RCR_TIMER_CFG_ENPTHRESH 0x0000000000008000ULL +#define RDC_RCR_TIMER_CFG_ENTIMEOUT 0x0000000000000040ULL +#define RDC_RCR_TIMER_CFG_TIMEOUT 0x000000000000003fULL +#define RDC_RCR_TIMER_CFG_TIMEOUT_SHIFT 0 + +#define RDC_MBX_UPD_CFG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0030) +#define RDC_MBX_UPD_CFG_ENABLE 0x0000000000010000ULL +#define RDC_MBX_UPD_CFG_PTHRESH 0x000000000000ffffULL + +#define RDC_KICK(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0038) +#define RDC_KICK_RCR_HEAD_UP_VLD 0x0000000080000000ULL +#define RDC_KICK_RCR_HEAD_WRAP 0x0000000040000000ULL +#define RDC_KICK_RCR_HEAD_WRAP_SHIFT 30 +#define RDC_KICK_RCR_HEAD_PT 0x000000003fffc000ULL +#define RDC_KICK_RCR_HEAD_PT_SHIFT 14 +#define RDC_KICK_RBR_TAIL_UP_VLD 0x0000000000002000ULL +#define RDC_KICK_RBR_TAIL_WRAP 0x0000000000001000ULL +#define RDC_KICK_RBR_TAIL 0x0000000000000fffULL + +#define RDC_ENT_MSK(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0040) +#define RDC_ENT_MSK_ALL 0x000000000000e3ffULL + +#define RDC_CTL_STAT(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0050) +#define RDC_CTL_STAT_TAIL_WRAP 0x4000000000000000ULL +#define RDC_CTL_STAT_TAIL_WRAP_SHIFT 62 +#define RDC_CTL_STAT_TAIL 0x3fffc00000000000ULL +#define RDC_CTL_STAT_TAIL_SHIFT 46 +#define RDC_CTL_STAT_RST 0x0000000080000000ULL +#define RDC_CTL_STAT_RST_STATE 0x0000000040000000ULL +#define RDC_CTL_STAT_STOP_N_GO 0x0000000010000000ULL +#define RDC_CTL_STAT_SNG_STATE 0x0000000008000000ULL +#define RDC_CTL_STAT_BLOCKING_MODE 0x0000000000010000ULL +#define RDC_CTL_STAT_MBOXTHRES 0x0000000000008000ULL +#define RDC_CTL_STAT_RCRTHRES 0x0000000000004000ULL +#define RDC_CTL_STAT_RCRTO 0x0000000000002000ULL +#define RDC_CTL_STAT_PKTCNT_OVERFLOW 0x0000000000001000ULL +#define RDC_CTL_STAT_DROPCNT_OVERFLOW 0x0000000000000800ULL +#define RDC_CTL_STAT_RBR_EMPTY 0x0000000000000400ULL +#define RDC_CTL_STAT_FIFO_ERR 0x0000000000000200ULL +#define RDC_CTL_STAT_RCR_SHADOW_FULL 0x0000000000000100ULL +#define RDC_CTL_STAT_REQ_REJECT 0x0000000000000080ULL +#define RDC_CTL_STAT_RBR_TIMEOUT 0x0000000000000040ULL +#define RDC_CTL_STAT_RSP_DAT_ERR 0x0000000000000020ULL +#define RDC_CTL_STAT_RCR_ACK_ERR 0x0000000000000010ULL +#define RDC_CTL_STAT_RCR_SHA_PAR 0x0000000000000008ULL +#define RDC_CTL_STAT_RBR_PRE_PAR 0x0000000000000004ULL +#define RDC_CTL_STAT_RCR_UNDERFLOW 0x0000000000000002ULL +#define RDC_CTL_STAT_RBR_OVERFLOW 0x0000000000000001ULL + +#define RDC_CTL_STAT_CHAN_FATAL (RDC_CTL_STAT_REQ_REJECT | \ + RDC_CTL_STAT_RBR_TIMEOUT | \ + RDC_CTL_STAT_RSP_DAT_ERR | \ + RDC_CTL_STAT_RCR_ACK_ERR | \ + RDC_CTL_STAT_RCR_SHA_PAR | \ + RDC_CTL_STAT_RBR_PRE_PAR | \ + RDC_CTL_STAT_RCR_UNDERFLOW | \ + RDC_CTL_STAT_RBR_OVERFLOW) + +#define RDC_CTL_STAT_WRITE_CLEAR_INT (RDC_CTL_STAT_BLOCKING_MODE | \ + RDC_CTL_STAT_MBOXTHRES | \ + RDC_CTL_STAT_RCRTHRES | \ + RDC_CTL_STAT_RCRTO | \ + RDC_CTL_STAT_PKTCNT_OVERFLOW | \ + RDC_CTL_STAT_DROPCNT_OVERFLOW | \ + RDC_CTL_STAT_RBR_EMPTY) + +#define RDC_CTL_STAT_WRITE_CLEAR_ERRS (RDC_CTL_STAT_FIFO_ERR | \ + RDC_CTL_STAT_RCR_SHADOW_FULL) + +#define RDC_CTL_STAT_DBG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0058) +#define RDC_FLSH(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0060) + +#define RDC_PKTCNT(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0078) +#define RDC_PKTCNT_COUNT 0x000000007fffffffULL + +#define RDC_PKTDROP(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0080) +#define RDC_PKTDROP_COUNT 0x000000007fffffffULL + +#define RDC_RNG_ERR_LOG(CHAN_BASE) (RXDMA_BASE + CHAN_BASE + 0x0088) + +/* Logical devices and device groups */ +#define LDN_RXDMA(CHAN) (0 + (CHAN)) +#define LDN_TXDMA(CHAN) (4 + (CHAN)) +#define LDN_RSV 8 +#define LDN_MAILBOX 9 +#define LDN_RXVMAC 10 +#define LDN_TXVMAC 11 +#define LDN_VNI_ERROR 12 +#define LDN_MAX LDN_VNI_ERROR + +#define SXGE_LDG_MIN 0 +#define SXGE_LDG_MAX 15 +#define SXGE_NUM_LDG 16 + +/* RCR Completion Ring */ +#define RCR_ENTRY_MULTI 0x8000000000000000ULL +#define RCR_ENTRY_PKT_TYPE 0x7c00000000000000ULL +#define RCR_ENTRY_PKT_TYPE_SHIFT 58 +#define RCR_ENTRY_PKT_ERR 0x0380000000000000ULL +#define RCR_ENTRY_PKT_ERR_SHIFT 55 +#define RCR_ENTRY_PKT_CLASS_CODE 0x0003e00000000000ULL +#define RCR_ENTRY_PKT_CLASS_CODE_SHIFT 45 +#define RCR_ENTRY_PROMISC 0x0000100000000000ULL +#define RCR_ENTRY_RSS_HASH 0x0000080000000000ULL +#define RCR_ENTRY_TCAM_HIT 0x0000040000000000ULL +#define RCR_ENTRY_PKTBUFSZ 0x0000030000000000ULL +#define RCR_ENTRY_PKTBUFSZ_SHIFT 40 +#define RCR_ENTRY_PKT_SEG_LEN 0x000000fffc000000ULL +#define RCR_ENTRY_PKT_SEG_LEN_SHIFT 26 +#define RCR_ENTRY_LAST_PKT_PER_BUF 0x0000000002000000ULL +#define RCR_ENTRY_SUBINDEX 0x0000000001ff0000ULL +#define RCR_ENTRY_SUBINDEX_SHIFT 16 +#define RCR_ENTRY_INDEX 0x000000000000ffffULL + +#define CLS_CODE_TCP_IPV4 0x8 +#define CLS_CODE_UDP_IPV4 0x9 +#define CLS_CODE_TCP_IPV6 0xc +#define CLS_CODE_UDP_IPV6 0xd +#define CLS_CODE_TCP_UDP 0xd + +/* host/eps mbox */ +#define SXGE_MB_STAT (STAND_RESOURCE_BASE + 0xC0) +#define SXGE_MB_STAT_OMB_ECC_ERR 0x0000000000000100ULL +#define SXGE_MB_STAT_IMB_ECC_ERR 0x0000000000000080ULL +#define SXGE_MB_STAT_FUNC_RST 0x0000000000000040ULL +#define SXGE_MB_STAT_FUNC_RST_DONE 0x0000000000000020ULL +#define SXGE_MB_STAT_OMB_OVL 0x0000000000000010ULL +#define SXGE_MB_STAT_IMB_FULL 0x0000000000000008ULL +#define SXGE_MB_STAT_OMB_ACKED 0x0000000000000004ULL +#define SXGE_MB_STAT_OMB_FAILED 0x0000000000000002ULL +#define SXGE_MB_STAT_OMB_FULL 0x0000000000000001ULL + +#define SXGE_MB_MSK (STAND_RESOURCE_BASE + 0xC8) +#define SXGE_MB_MSK_OMB_ECC_ERR_INT_MSK 0x0000000000000100ULL +#define SXGE_MB_MSK_IMB_ECC_ERR_INT_MSK 0x0000000000000080ULL +#define SXGE_MB_MSK_FUNC_RST_DONE_INT_MSK 0x0000000000000020ULL +#define SXGE_MB_MSK_OMB_OVL_INT_MSK 0x0000000000000010ULL +#define SXGE_MB_MSK_IMB_FULL_INT_MSK 0x0000000000000008ULL +#define SXGE_MB_MSK_OMB_ACK_INT_MSK 0x0000000000000004ULL +#define SXGE_MB_MSK_OMB_FAILED_INT_MSK 0x0000000000000002ULL + +#define SXGE_OMB(entry) (STAND_RESOURCE_BASE + 0x80 + (8 * entry)) +#define SXGE_IMB(entry) (STAND_RESOURCE_BASE + 0xD0 + (8 * entry)) +#define SXGE_IMB_ACK (STAND_RESOURCE_BASE + 0x110) +#define SXGE_IMB_ACK_IMB_NACK 0x0000000000000002ULL +#define SXGE_IMB_ACK_IMB_ACK 0x0000000000000001ULL + +/* Host/EPS MBOX related data structs */ +#define SXGE_MB_MAX_LEN 0x7 /* Number of 64-bit words */ +#define SXGE_MB_GET_CAPAB 0x100 +#define SXGE_MB_GET_L2_ADDR_CAP (SXGE_MB_GET_CAPAB + 0x01) +#define SXGE_MB_GET_TCAM_CAP (SXGE_MB_GET_CAPAB + 0x02) + +#define SXGE_MB_CLS_OPS 0x200 +#define SXGE_MB_L2_ADDR_ADD (SXGE_MB_CLS_OPS + 0x0) +#define SXGE_MB_L2_ADDR_REM (SXGE_MB_CLS_OPS + 0x1) +#define SXGE_MB_L2_MCAST_ADD (SXGE_MB_CLS_OPS + 0x2) +#define SXGE_MB_L2_MCAST_REM (SXGE_MB_CLS_OPS + 0x3) +#define SXGE_MB_VLAN_ADD (SXGE_MB_CLS_OPS + 0x4) +#define SXGE_MB_VLAN_REMOVE (SXGE_MB_CLS_OPS + 0x5) +#define SXGE_MB_L3L4_TCAM_ADD (SXGE_MB_CLS_OPS + 0x6) +#define SXGE_MB_L3L4_TCAM_REMOVE (SXGE_MB_CLS_OPS + 0x7) +#define SXGE_MB_RSS_HASH (SXGE_MB_CLS_OPS + 0x8) +#define SXGE_MB_LINK_SPEED (SXGE_MB_CLS_OPS + 0x9) + +#define SXGE_MB_REQUEST 0x01 +#define SXGE_MB_RESPONSE 0x02 + +#define MB_TAG_LEN 0x00000000000000ffULL +#define MB_TAG_LEN_SHIFT 0 +#define MB_TAG_TYPE 0x00000000ffff0000ULL +#define MB_TAG_TYPE_SHIFT 16 +#define MB_TAG_REQ 0x0000ffff00000000ULL +#define MB_TAG_REQ_SHIFT 32 +#define MB_TAG_SEQ 0xffff000000000000ULL +#define MB_TAG_SEQ_SHIFT 48 + +struct sxge_mb_msg { + u64 len; + u64 msg_data[SXGE_MB_MAX_LEN]; +}; + +struct l2_address_req { + u64 mb_tag; + u64 addr; + u64 mask; + u64 slot; + u64 rsv1; + u64 rsv2; +}; + +#define SXGE_MB_L2_ADDR_REQ_LEN (sizeof(struct l2_address_req)/sizeof(u64)) + +struct mb_cap { + u64 mb_tag; + u64 n_u_addrs; + u64 n_m_addrs; + u64 link_speed; + u64 rsv1; + u64 rsv2; + u64 rsv3; +}; + +#define SXGE_MB_CAP_LEN (sizeof (struct mb_cap) / sizeof (u64)) +#define SXGE_MB_PCS_MODE_SHIFT 16 +#define SXGE_MB_PCS_MODE_MASK 0x30000 +#define SXGE_MB_PCS_MODE_KR 0x0 +#define SXGE_MB_PCS_MODE_KX4 0x1 +#define SXGE_MB_PCS_MODE_X 0x2 +#define SXGE_MB_PCS_MODE_KR4 0x3 +#define SXGE_MB_40G_MODE_INDEX 4 + +#define SXGE_MAX_TCAM_ENTRY_PER_FUNC 4 + +/* TX related data structs */ +struct tx_pkt_hdr { + __le64 flags; +#define TXHDR_PAD 0x0000000000000007ULL +#define TXHDR_PAD_SHIFT 0 +#define TXHDR_FC_OFFSET 0x000000000000ff00ULL +#define TXHDR_FC_OFFSET_SHIFT 8 +#define TXHDR_LEN 0x000000003fff0000ULL +#define TXHDR_LEN_SHIFT 16 +#define TXHDR_L4STUFF 0x0000003f00000000ULL +#define TXHDR_L4STUFF_SHIFT 32 +#define TXHDR_L4START 0x00003f0000000000ULL +#define TXHDR_L4START_SHIFT 40 +#define TXHDR_L3START 0x000f000000000000ULL +#define TXHDR_L3START_SHIFT 48 +#define TXHDR_IHL 0x00f0000000000000ULL +#define TXHDR_IHL_SHIFT 52 +#define TXHDR_VLAN 0x0100000000000000ULL +#define TXHDR_LLC 0x0200000000000000ULL +#define TXHDR_PKT_TYPE 0x0400000000000000ULL +#define TXHDR_IP_VER 0x2000000000000000ULL +#define TXHDR_CSUM_NONE 0x0000000000000000ULL +#define TXHDR_CSUM_TCP 0x4000000000000000ULL +#define TXHDR_CSUM_UDP 0x8000000000000000ULL +#define TXHDR_CSUM_SCTP 0xc000000000000000ULL + __le64 resv; +}; + +#define TX_DESC_SOP 0x8000000000000000ULL +#define TX_DESC_MARK 0x4000000000000000ULL +#define TX_DESC_NUM_PTR 0x3c00000000000000ULL +#define TX_DESC_NUM_PTR_SHIFT 58 +#define TX_DESC_CKSUM_EN 0x0200000000000000ULL +#define TX_DESC_CKSUM_EN_SHIFT 57 +#define TX_DESC_TR_LEN 0x01fff00000000000ULL +#define TX_DESC_TR_LEN_SHIFT 44 +#define TX_DESC_SAD 0x00000fffffffffffULL +#define TX_DESC_SAD_SHIFT 0 + +struct tx_buff_info { + struct sk_buff *skb; + u64 mapping; +}; + +struct txdma_mailbox { + __le64 tx_dma_pre_st; + __le64 tx_cs; + __le64 tx_ring_kick; + __le64 tx_ring_hdl; + __le64 resv[4]; +} __attribute__((aligned(64))); + +#define MAX_TX_RING_SIZE 1024 +#define MAX_TX_DESC_LEN 4076 + +struct tx_ring_info { + struct tx_buff_info tx_buffs[MAX_TX_RING_SIZE]; + struct sxge *sxgep; + u8 vni; + u8 vmac; + u32 tdc_base; + u32 vmac_base; + u64 tx_cs; + int pending; + int prod; + int cons; + int wrap_bit; + u16 last_pkt_cnt; + u16 tx_channel; + u16 mark_counter; + u16 mark_freq; + u16 mark_pending; + u16 __pad; + struct txdma_mailbox *mbox; + __le64 *descr; + + u64 tx_packets; + u64 tx_bytes; + u64 tx_errors; + + u64 mbox_dma; + u64 descr_dma; + int max_burst; + u64 tdc_prsr_en; +}; + +#define NEXT_TX(tp, index) \ + (((index) + 1) < (tp)->pending ? ((index) + 1) : 0) + +static inline int sxge_tx_avail(struct tx_ring_info *tp) +{ + return tp->pending - + ((tp->prod - tp->cons) & (MAX_TX_RING_SIZE - 1)); +} + +struct rxdma_mailbox { + __le64 rx_dma_ctl_stat; + __le64 rbr_stat; + __le32 rbr_tail_rcr_head; + __le32 resv0; + __le64 resv1[5]; +} __attribute__((aligned(64))); + +#define MAX_RBR_RING_SIZE 1024 +#define MAX_RCR_RING_SIZE (MAX_RBR_RING_SIZE * 2) + +#define RBR_REFILL_MIN 16 + +#define RX_SKB_ALLOC_SIZE (128 + NET_IP_ALIGN) + +struct rx_ring_info { + struct sxge *sxgep; + u8 rx_channel; + u8 vni; + u8 vmac; + u32 rdc_base; + u32 vmac_base; + u16 rbr_block_size; + u16 rbr_blocks_per_page; + u16 rbr_sizes[4]; + unsigned int rcr_index; + u64 rcr_head_wrap; + u64 rcr_tail_wrap; + unsigned int rcr_table_size; + unsigned int rbr_index; + u64 rbr_tail; + u64 rbr_tail_wrap; + u64 rbr_head_wrap; + unsigned int rbr_pending; + unsigned int rbr_refill_pending; + unsigned int rbr_kick_thresh; + unsigned int rbr_table_size; + struct page **rxpage; + pgoff_t saved_base[MAX_RBR_RING_SIZE * 2]; + struct rxdma_mailbox *mbox; + __le64 *rcr; + __le64 *rbr; +#define RBR_DESCR_INDEX 0x0ffff00000000000ULL +#define RBR_DESCR_INDEX_SHIFT 44 +#define RBR_DESCR_ADDR 0x00000000ffffffffULL +#define RBR_DESCR_ADDR_SHIFT 12 + + u64 rx_packets; + u64 rx_bytes; + u64 rx_dropped; + u64 rx_errors; + u64 rx_hw_pktcnt; + u64 rx_hw_pktdrop; + u64 rx_rbr_empty; + u64 rx_fifo_error; + u64 rx_rcr_shadow_full; + + u64 mbox_dma; + u64 rcr_dma; + u64 rbr_dma; + + /* interrupt mitigation */ + int rcr_pkt_threshold; + int rcr_timeout; +}; + +#define NEXT_RCR(rp, index) \ + (((index) + 1) < (rp)->rcr_table_size ? ((index) + 1) : 0) +#define NEXT_RBR(rp, index) \ + (((index) + 1) < (rp)->rbr_table_size ? ((index) + 1) : 0) + +#define SPEED_40000 40000 +#define SPEED_4000 4000 + +struct sxge_link_config { + u32 supported; + + /* Describes what we're trying to get. */ + u32 advertising; + u16 speed; + u8 duplex; + u8 autoneg; + + /* Describes what we actually have. */ + u32 active_advertising; + u16 active_speed; + u8 active_duplex; + u8 active_autoneg; +}; + +struct sxge_ldg { + struct napi_struct napi; + struct sxge *sxgep; + u8 ldg_num; + u8 timer; + u64 v; + unsigned int irq; +}; + +struct sxge_vmac_stats { + u64 txvmac_frames; + u64 txvmac_bytes; + u64 txvmac_frame_cnt_ovl; + u64 txvmac_byte_cnt_ovl; + + u64 rxvmac_frames; + u64 rxvmac_bytes; + u64 rxvmac_drops; + u64 rxvmac_drop_bytes; + u64 rxvmac_mcasts; + u64 rxvmac_bcasts; + u64 rxvmac_frames_cnt_ovl; + u64 rxvmac_byte_cnt_ovl; + u64 rxvmac_drop_cnt_ovl; + u64 rxvmac_drop_byte_ovl; + u64 rxvmac_mcast_frame_cnt_ovl; + u64 rxvmac_bcast_frame_cnt_ovl; + u64 rxvmac_link_up; + u64 rxvmac_link_down; + u64 rxvmac_link_state; +}; + +struct sxge { + void __iomem *regs; + struct net_device *dev; + struct pci_dev *pdev; + struct device *device; + u32 flags; +#define SXGE_FLAGS_MSIX 0x00400000 /* MSI-X in use */ +#define SXGE_FLAGS_MCAST 0x00200000 +#define SXGE_FLAGS_PROMISC 0x00100000 +#define SXGE_FLAGS_SRIOV 0x00800000 +#define SXGE_FLAGS_HW_INIT 0x01000000 + + u32 msg_enable; + /* Protects hw programming, and ring state. */ + spinlock_t lock; + + const struct sxge_ops *ops; + struct sxge_vmac_stats vmac_stats; + + /* RDAT resource */ + u8 piobar_resource[MAX_PIOBAR_RESOURCE]; + u8 vni; + u8 vmac; + + struct rx_ring_info *rx_rings; + struct tx_ring_info *tx_rings; + int num_rings; + + struct sxge_ldg ldg[SXGE_NUM_LDG]; + int num_ldg; + u8 ldg_map[LDN_MAX + 1]; + u8 intmgmt_nf; + + struct sxge_link_config link_config; + + struct work_struct reset_task; + u8 devfn; + u8 dev_busnum; + u64 sxge_mb_stat; +}; + +struct sxge_ops { + void *(*alloc_coherent)(struct device *dev, size_t size, + u64 *handle, gfp_t flag); + void (*free_coherent)(struct device *dev, size_t size, + void *cpu_addr, u64 handle); + u64 (*map_page)(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction); + void (*unmap_page)(struct device *dev, u64 dma_address, + size_t size, enum dma_data_direction direction); + u64 (*map_single)(struct device *dev, void *cpu_addr, + size_t size, + enum dma_data_direction direction); + void (*unmap_single)(struct device *dev, u64 dma_address, + size_t size, enum dma_data_direction direction); +}; + +#endif /* _SXGE_H */ diff --git a/drivers/net/sxgevf/Kconfig b/drivers/net/sxgevf/Kconfig new file mode 100644 index 0000000000000..59ef941471dd7 --- /dev/null +++ b/drivers/net/sxgevf/Kconfig @@ -0,0 +1,6 @@ +config SXGEVF + tristate "SXGEVF SOL ethernet driver " + depends on PCI + ---help--- + This driver supports the SXGEVF SOL 40Gb Ethernet driver. + diff --git a/drivers/net/sxgevf/Makefile b/drivers/net/sxgevf/Makefile new file mode 100644 index 0000000000000..4490f2e04a9ad --- /dev/null +++ b/drivers/net/sxgevf/Makefile @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -DSXGE_VF -I../sxge/ +obj-$(CONFIG_SXGEVF) += sxgevf.o + + + diff --git a/drivers/net/sxgevf/sxgevf.c b/drivers/net/sxgevf/sxgevf.c new file mode 100644 index 0000000000000..24990dc40c494 --- /dev/null +++ b/drivers/net/sxgevf/sxgevf.c @@ -0,0 +1,3 @@ +/* code for sxgevf is maintained in sxge.c with -DSXGE_VF */ + +#include "../sxge/sxge.c"