#include <linux/log2.h>
#include <linux/jiffies.h>
#include <linux/crc32.h>
+#include <linux/list.h>
#include <linux/io.h>
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
-#ifndef DMA_44BIT_MASK
-#define DMA_44BIT_MASK 0x00000fffffffffffULL
-#endif
-
#ifndef readq
static u64 readq(void __iomem *reg)
{
err = mdio_read(np, np->phy_addr,
BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
- if (err < 0)
+ if (err < 0 || err == 0xffff)
return err;
err |= BMCR_RESET;
err = mdio_write(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
BCM8704_PMD_RCV_SIGDET);
- if (err < 0)
+ if (err < 0 || err == 0xffff)
goto out;
if (!(err & PMD_RCV_SIGDET_GLOBAL)) {
err = 0;
out:
*link_up_p = link_up;
- if (np->flags & NIU_FLAGS_HOTPLUG_PHY)
- err = 0;
return err;
}
if (phy_present != phy_present_prev) {
/* state change */
if (phy_present) {
+ /* A NEM was just plugged in */
np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT;
if (np->phy_ops->xcvr_init)
err = np->phy_ops->xcvr_init(np);
if (err) {
+ err = mdio_read(np, np->phy_addr,
+ BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
+ if (err == 0xffff) {
+ /* No mdio, back-to-back XAUI */
+ goto out;
+ }
/* debounce */
np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
}
np->dev->name);
}
}
- if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT)
+out:
+ if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) {
err = link_status_10g_bcm8706(np, link_up_p);
+ if (err == 0xffff) {
+ /* No mdio, back-to-back XAUI: it is C10NEM */
+ *link_up_p = 1;
+ np->link_config.active_speed = SPEED_10000;
+ np->link_config.active_duplex = DUPLEX_FULL;
+ }
+ }
}
spin_unlock_irqrestore(&np->lock, flags);
- return err;
+ return 0;
}
static int niu_link_status(struct niu *np, int *link_up_p)
.link_status = link_status_10g_hotplug,
};
+static const struct niu_phy_ops phy_ops_niu_10g_hotplug = {
+ .serdes_init = serdes_init_niu_10g_fiber,
+ .xcvr_init = xcvr_init_10g_bcm8706,
+ .link_status = link_status_10g_hotplug,
+};
+
static const struct niu_phy_ops phy_ops_10g_copper = {
.serdes_init = serdes_init_10g,
.link_status = link_status_10g, /* XXX */
.phy_addr_base = 8,
};
+static const struct niu_phy_template phy_template_niu_10g_hotplug = {
+ .ops = &phy_ops_niu_10g_hotplug,
+ .phy_addr_base = 8,
+};
+
static const struct niu_phy_template phy_template_10g_copper = {
.ops = &phy_ops_10g_copper,
.phy_addr_base = 10,
case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
/* 10G Fiber */
default:
- tp = &phy_template_niu_10g_fiber;
- phy_addr_off += np->port;
+ if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
+ tp = &phy_template_niu_10g_hotplug;
+ if (np->port == 0)
+ phy_addr_off = 8;
+ if (np->port == 1)
+ phy_addr_off = 12;
+ } else {
+ tp = &phy_template_niu_10g_fiber;
+ phy_addr_off += np->port;
+ }
break;
}
} else {
msleep(200);
}
err = niu_serdes_init(np);
- if (err)
+ if (err && !(np->flags & NIU_FLAGS_HOTPLUG_PHY))
return err;
msleep(200);
err = niu_xcvr_init(np);
- if (!err)
+ if (!err || (np->flags & NIU_FLAGS_HOTPLUG_PHY))
niu_link_status(np, &ignore);
return 0;
}
break;
udelay(1);
}
- if (limit < 0)
+ if (limit <= 0)
return -ENODEV;
return 0;
return 0;
}
-#if 0
static int tcam_user_ip_class_set(struct niu *np, unsigned long class,
int ipv6, u64 protocol_id,
u64 tos_mask, u64 tos_val)
return 0;
}
-#endif
static int tcam_early_init(struct niu *np)
{
return 0;
}
+/* Entries for the ports are interleaved in the TCAM */
+static u16 tcam_get_index(struct niu *np, u16 idx)
+{
+ /* One entry reserved for IP fragment rule */
+ if (idx >= (np->clas.tcam_sz - 1))
+ idx = 0;
+ return (np->clas.tcam_top + ((idx+1) * np->parent->num_ports));
+}
+
+static u16 tcam_get_size(struct niu *np)
+{
+ /* One entry reserved for IP fragment rule */
+ return np->clas.tcam_sz - 1;
+}
+
+static u16 tcam_get_valid_entry_cnt(struct niu *np)
+{
+ /* One entry reserved for IP fragment rule */
+ return np->clas.tcam_valid_entries - 1;
+}
+
static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
u32 offset, u32 size)
{
return num_rcr;
}
-static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp)
+static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
+ struct rx_ring_info *rp)
{
unsigned int index = rp->rcr_index;
struct sk_buff *skb;
rp->rcr_index = index;
skb_reserve(skb, NET_IP_ALIGN);
- __pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX));
+ __pskb_pull_tail(skb, min(len, VLAN_ETH_HLEN));
rp->rx_packets++;
rp->rx_bytes += skb->len;
skb->protocol = eth_type_trans(skb, np->dev);
skb_record_rx_queue(skb, rp->rx_channel);
- netif_receive_skb(skb);
+ napi_gro_receive(napi, skb);
return num_rcr;
}
}
}
-static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
+static int niu_rx_work(struct napi_struct *napi, struct niu *np,
+ struct rx_ring_info *rp, int budget)
{
int qlen, rcr_done = 0, work_done = 0;
struct rxdma_mailbox *mbox = rp->mbox;
rcr_done = work_done = 0;
qlen = min(qlen, budget);
while (work_done < qlen) {
- rcr_done += niu_process_rx_pkt(np, rp);
+ rcr_done += niu_process_rx_pkt(napi, np, rp);
work_done++;
}
if (rx_vec & (1 << rp->rx_channel)) {
int this_work_done;
- this_work_done = niu_rx_work(np, rp,
+ this_work_done = niu_rx_work(&lp->napi, np, rp,
budget);
budget -= this_work_done;
mp->rx_hist_cnt6 += RXMAC_HIST_CNT6_COUNT;
if (val & XRXMAC_STATUS_RXHIST7_CNT_EXP)
mp->rx_hist_cnt7 += RXMAC_HIST_CNT7_COUNT;
- if (val & XRXMAC_STAT_MSK_RXOCTET_CNT_EXP)
+ if (val & XRXMAC_STATUS_RXOCTET_CNT_EXP)
mp->rx_octets += RXMAC_BT_CNT_COUNT;
if (val & XRXMAC_STATUS_CVIOLERR_CNT_EXP)
mp->rx_code_violations += RXMAC_CD_VIO_CNT_COUNT;
{
u64 val = 0;
+ *ret = 0;
switch (rp->rbr_block_size) {
case 4 * 1024:
val |= (RBR_BLKSIZE_4K << RBR_CFIG_B_BLKSIZE_SHIFT);
struct niu_tcam_entry *tp;
int index, err;
- /* XXX fix this allocation scheme XXX */
- index = cp->tcam_index;
+ index = cp->tcam_top;
tp = &parent->tcam[index];
/* Note that the noport bit is the same in both ipv4 and
err = tcam_assoc_write(np, index, tp->assoc_data);
if (err)
return err;
+ tp->valid = 1;
+ cp->tcam_valid_entries++;
return 0;
}
/* The XMAC_MIN register only accepts values for TX min which
* have the low 3 bits cleared.
*/
- BUILD_BUG_ON(min & 0x7);
+ BUG_ON(min & 0x7);
if (np->flags & NIU_FLAGS_XMAC)
niu_init_tx_xmac(np, min, max);
struct niu *np = netdev_priv(dev);
int i, alt_cnt, err;
struct dev_addr_list *addr;
+ struct netdev_hw_addr *ha;
unsigned long flags;
u16 hash[16] = { 0, };
if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 0))
np->flags |= NIU_FLAGS_MCAST;
- alt_cnt = dev->uc_count;
+ alt_cnt = dev->uc.count;
if (alt_cnt > niu_num_alt_addr(np)) {
alt_cnt = 0;
np->flags |= NIU_FLAGS_PROMISC;
if (alt_cnt) {
int index = 0;
- for (addr = dev->uc_list; addr; addr = addr->next) {
- err = niu_set_alt_mac(np, index,
- addr->da_addr);
+ list_for_each_entry(ha, &dev->uc.list, list) {
+ err = niu_set_alt_mac(np, index, ha->addr);
if (err)
printk(KERN_WARNING PFX "%s: Error %d "
"adding alt mac %d\n",
return ret;
}
-static int niu_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t niu_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct niu *np = netdev_priv(dev);
unsigned long align, headroom;
netif_tx_wake_queue(txq);
}
- dev->trans_start = jiffies;
-
out:
return NETDEV_TX_OK;
return 0;
}
+static void niu_ethflow_to_l3proto(int flow_type, u8 *pid)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case TCP_V6_FLOW:
+ *pid = IPPROTO_TCP;
+ break;
+ case UDP_V4_FLOW:
+ case UDP_V6_FLOW:
+ *pid = IPPROTO_UDP;
+ break;
+ case SCTP_V4_FLOW:
+ case SCTP_V6_FLOW:
+ *pid = IPPROTO_SCTP;
+ break;
+ case AH_V4_FLOW:
+ case AH_V6_FLOW:
+ *pid = IPPROTO_AH;
+ break;
+ case ESP_V4_FLOW:
+ case ESP_V6_FLOW:
+ *pid = IPPROTO_ESP;
+ break;
+ default:
+ *pid = 0;
+ break;
+ }
+}
+
+static int niu_class_to_ethflow(u64 class, int *flow_type)
+{
+ switch (class) {
+ case CLASS_CODE_TCP_IPV4:
+ *flow_type = TCP_V4_FLOW;
+ break;
+ case CLASS_CODE_UDP_IPV4:
+ *flow_type = UDP_V4_FLOW;
+ break;
+ case CLASS_CODE_AH_ESP_IPV4:
+ *flow_type = AH_V4_FLOW;
+ break;
+ case CLASS_CODE_SCTP_IPV4:
+ *flow_type = SCTP_V4_FLOW;
+ break;
+ case CLASS_CODE_TCP_IPV6:
+ *flow_type = TCP_V6_FLOW;
+ break;
+ case CLASS_CODE_UDP_IPV6:
+ *flow_type = UDP_V6_FLOW;
+ break;
+ case CLASS_CODE_AH_ESP_IPV6:
+ *flow_type = AH_V6_FLOW;
+ break;
+ case CLASS_CODE_SCTP_IPV6:
+ *flow_type = SCTP_V6_FLOW;
+ break;
+ case CLASS_CODE_USER_PROG1:
+ case CLASS_CODE_USER_PROG2:
+ case CLASS_CODE_USER_PROG3:
+ case CLASS_CODE_USER_PROG4:
+ *flow_type = IP_USER_FLOW;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
static int niu_ethflow_to_class(int flow_type, u64 *class)
{
switch (flow_type) {
case UDP_V4_FLOW:
*class = CLASS_CODE_UDP_IPV4;
break;
- case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
*class = CLASS_CODE_AH_ESP_IPV4;
break;
case SCTP_V4_FLOW:
case UDP_V6_FLOW:
*class = CLASS_CODE_UDP_IPV6;
break;
- case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
*class = CLASS_CODE_AH_ESP_IPV6;
break;
case SCTP_V6_FLOW:
{
u64 ethflow = 0;
- if (flow_key & FLOW_KEY_PORT)
- ethflow |= RXH_DEV_PORT;
if (flow_key & FLOW_KEY_L2DA)
ethflow |= RXH_L2DA;
if (flow_key & FLOW_KEY_VLAN)
{
u64 key = 0;
- if (ethflow & RXH_DEV_PORT)
- key |= FLOW_KEY_PORT;
if (ethflow & RXH_L2DA)
key |= FLOW_KEY_L2DA;
if (ethflow & RXH_VLAN)
}
-static int niu_get_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
+static int niu_get_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc)
{
- struct niu *np = netdev_priv(dev);
u64 class;
- cmd->data = 0;
+ nfc->data = 0;
- if (!niu_ethflow_to_class(cmd->flow_type, &class))
+ if (!niu_ethflow_to_class(nfc->flow_type, &class))
return -EINVAL;
if (np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] &
TCAM_KEY_DISC)
- cmd->data = RXH_DISCARD;
+ nfc->data = RXH_DISCARD;
else
-
- cmd->data = niu_flowkey_to_ethflow(np->parent->flow_key[class -
+ nfc->data = niu_flowkey_to_ethflow(np->parent->flow_key[class -
CLASS_CODE_USER_PROG1]);
return 0;
}
-static int niu_set_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
+static void niu_get_ip4fs_from_tcam_key(struct niu_tcam_entry *tp,
+ struct ethtool_rx_flow_spec *fsp)
+{
+
+ fsp->h_u.tcp_ip4_spec.ip4src = (tp->key[3] & TCAM_V4KEY3_SADDR) >>
+ TCAM_V4KEY3_SADDR_SHIFT;
+ fsp->h_u.tcp_ip4_spec.ip4dst = (tp->key[3] & TCAM_V4KEY3_DADDR) >>
+ TCAM_V4KEY3_DADDR_SHIFT;
+ fsp->m_u.tcp_ip4_spec.ip4src = (tp->key_mask[3] & TCAM_V4KEY3_SADDR) >>
+ TCAM_V4KEY3_SADDR_SHIFT;
+ fsp->m_u.tcp_ip4_spec.ip4dst = (tp->key_mask[3] & TCAM_V4KEY3_DADDR) >>
+ TCAM_V4KEY3_DADDR_SHIFT;
+
+ fsp->h_u.tcp_ip4_spec.ip4src =
+ cpu_to_be32(fsp->h_u.tcp_ip4_spec.ip4src);
+ fsp->m_u.tcp_ip4_spec.ip4src =
+ cpu_to_be32(fsp->m_u.tcp_ip4_spec.ip4src);
+ fsp->h_u.tcp_ip4_spec.ip4dst =
+ cpu_to_be32(fsp->h_u.tcp_ip4_spec.ip4dst);
+ fsp->m_u.tcp_ip4_spec.ip4dst =
+ cpu_to_be32(fsp->m_u.tcp_ip4_spec.ip4dst);
+
+ fsp->h_u.tcp_ip4_spec.tos = (tp->key[2] & TCAM_V4KEY2_TOS) >>
+ TCAM_V4KEY2_TOS_SHIFT;
+ fsp->m_u.tcp_ip4_spec.tos = (tp->key_mask[2] & TCAM_V4KEY2_TOS) >>
+ TCAM_V4KEY2_TOS_SHIFT;
+
+ switch (fsp->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ fsp->h_u.tcp_ip4_spec.psrc =
+ ((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16;
+ fsp->h_u.tcp_ip4_spec.pdst =
+ ((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff;
+ fsp->m_u.tcp_ip4_spec.psrc =
+ ((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16;
+ fsp->m_u.tcp_ip4_spec.pdst =
+ ((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff;
+
+ fsp->h_u.tcp_ip4_spec.psrc =
+ cpu_to_be16(fsp->h_u.tcp_ip4_spec.psrc);
+ fsp->h_u.tcp_ip4_spec.pdst =
+ cpu_to_be16(fsp->h_u.tcp_ip4_spec.pdst);
+ fsp->m_u.tcp_ip4_spec.psrc =
+ cpu_to_be16(fsp->m_u.tcp_ip4_spec.psrc);
+ fsp->m_u.tcp_ip4_spec.pdst =
+ cpu_to_be16(fsp->m_u.tcp_ip4_spec.pdst);
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ fsp->h_u.ah_ip4_spec.spi =
+ (tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT;
+ fsp->m_u.ah_ip4_spec.spi =
+ (tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT;
+
+ fsp->h_u.ah_ip4_spec.spi =
+ cpu_to_be32(fsp->h_u.ah_ip4_spec.spi);
+ fsp->m_u.ah_ip4_spec.spi =
+ cpu_to_be32(fsp->m_u.ah_ip4_spec.spi);
+ break;
+ case IP_USER_FLOW:
+ fsp->h_u.usr_ip4_spec.l4_4_bytes =
+ (tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT;
+ fsp->m_u.usr_ip4_spec.l4_4_bytes =
+ (tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
+ TCAM_V4KEY2_PORT_SPI_SHIFT;
+
+ fsp->h_u.usr_ip4_spec.l4_4_bytes =
+ cpu_to_be32(fsp->h_u.usr_ip4_spec.l4_4_bytes);
+ fsp->m_u.usr_ip4_spec.l4_4_bytes =
+ cpu_to_be32(fsp->m_u.usr_ip4_spec.l4_4_bytes);
+
+ fsp->h_u.usr_ip4_spec.proto =
+ (tp->key[2] & TCAM_V4KEY2_PROTO) >>
+ TCAM_V4KEY2_PROTO_SHIFT;
+ fsp->m_u.usr_ip4_spec.proto =
+ (tp->key_mask[2] & TCAM_V4KEY2_PROTO) >>
+ TCAM_V4KEY2_PROTO_SHIFT;
+
+ fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+ break;
+ default:
+ break;
+ }
+}
+
+static int niu_get_ethtool_tcam_entry(struct niu *np,
+ struct ethtool_rxnfc *nfc)
+{
+ struct niu_parent *parent = np->parent;
+ struct niu_tcam_entry *tp;
+ struct ethtool_rx_flow_spec *fsp = &nfc->fs;
+ u16 idx;
+ u64 class;
+ int ret = 0;
+
+ idx = tcam_get_index(np, (u16)nfc->fs.location);
+
+ tp = &parent->tcam[idx];
+ if (!tp->valid) {
+ pr_info(PFX "niu%d: %s entry [%d] invalid for idx[%d]\n",
+ parent->index, np->dev->name, (u16)nfc->fs.location, idx);
+ return -EINVAL;
+ }
+
+ /* fill the flow spec entry */
+ class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >>
+ TCAM_V4KEY0_CLASS_CODE_SHIFT;
+ ret = niu_class_to_ethflow(class, &fsp->flow_type);
+
+ if (ret < 0) {
+ pr_info(PFX "niu%d: %s niu_class_to_ethflow failed\n",
+ parent->index, np->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (fsp->flow_type == AH_V4_FLOW || fsp->flow_type == AH_V6_FLOW) {
+ u32 proto = (tp->key[2] & TCAM_V4KEY2_PROTO) >>
+ TCAM_V4KEY2_PROTO_SHIFT;
+ if (proto == IPPROTO_ESP) {
+ if (fsp->flow_type == AH_V4_FLOW)
+ fsp->flow_type = ESP_V4_FLOW;
+ else
+ fsp->flow_type = ESP_V6_FLOW;
+ }
+ }
+
+ switch (fsp->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ niu_get_ip4fs_from_tcam_key(tp, fsp);
+ break;
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ /* Not yet implemented */
+ ret = -EINVAL;
+ break;
+ case IP_USER_FLOW:
+ niu_get_ip4fs_from_tcam_key(tp, fsp);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret < 0)
+ goto out;
+
+ if (tp->assoc_data & TCAM_ASSOCDATA_DISC)
+ fsp->ring_cookie = RX_CLS_FLOW_DISC;
+ else
+ fsp->ring_cookie = (tp->assoc_data & TCAM_ASSOCDATA_OFFSET) >>
+ TCAM_ASSOCDATA_OFFSET_SHIFT;
+
+ /* put the tcam size here */
+ nfc->data = tcam_get_size(np);
+out:
+ return ret;
+}
+
+static int niu_get_ethtool_tcam_all(struct niu *np,
+ struct ethtool_rxnfc *nfc,
+ u32 *rule_locs)
+{
+ struct niu_parent *parent = np->parent;
+ struct niu_tcam_entry *tp;
+ int i, idx, cnt;
+ u16 n_entries;
+ unsigned long flags;
+
+
+ /* put the tcam size here */
+ nfc->data = tcam_get_size(np);
+
+ niu_lock_parent(np, flags);
+ n_entries = nfc->rule_cnt;
+ for (cnt = 0, i = 0; i < nfc->data; i++) {
+ idx = tcam_get_index(np, i);
+ tp = &parent->tcam[idx];
+ if (!tp->valid)
+ continue;
+ rule_locs[cnt] = i;
+ cnt++;
+ }
+ niu_unlock_parent(np, flags);
+
+ if (n_entries != cnt) {
+ /* print warning, this should not happen */
+ pr_info(PFX "niu%d: %s In niu_get_ethtool_tcam_all, "
+ "n_entries[%d] != cnt[%d]!!!\n\n",
+ np->parent->index, np->dev->name, n_entries, cnt);
+ }
+
+ return 0;
+}
+
+static int niu_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+ void *rule_locs)
{
struct niu *np = netdev_priv(dev);
+ int ret = 0;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXFH:
+ ret = niu_get_hash_opts(np, cmd);
+ break;
+ case ETHTOOL_GRXRINGS:
+ cmd->data = np->num_rx_rings;
+ break;
+ case ETHTOOL_GRXCLSRLCNT:
+ cmd->rule_cnt = tcam_get_valid_entry_cnt(np);
+ break;
+ case ETHTOOL_GRXCLSRULE:
+ ret = niu_get_ethtool_tcam_entry(np, cmd);
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ ret = niu_get_ethtool_tcam_all(np, cmd, (u32 *)rule_locs);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int niu_set_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc)
+{
u64 class;
u64 flow_key = 0;
unsigned long flags;
- if (!niu_ethflow_to_class(cmd->flow_type, &class))
+ if (!niu_ethflow_to_class(nfc->flow_type, &class))
return -EINVAL;
if (class < CLASS_CODE_USER_PROG1 ||
class > CLASS_CODE_SCTP_IPV6)
return -EINVAL;
- if (cmd->data & RXH_DISCARD) {
+ if (nfc->data & RXH_DISCARD) {
niu_lock_parent(np, flags);
flow_key = np->parent->tcam_key[class -
CLASS_CODE_USER_PROG1];
}
}
- if (!niu_ethflow_to_flowkey(cmd->data, &flow_key))
+ if (!niu_ethflow_to_flowkey(nfc->data, &flow_key))
return -EINVAL;
niu_lock_parent(np, flags);
return 0;
}
+static void niu_get_tcamkey_from_ip4fs(struct ethtool_rx_flow_spec *fsp,
+ struct niu_tcam_entry *tp,
+ int l2_rdc_tab, u64 class)
+{
+ u8 pid = 0;
+ u32 sip, dip, sipm, dipm, spi, spim;
+ u16 sport, dport, spm, dpm;
+
+ sip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4src);
+ sipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4src);
+ dip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4dst);
+ dipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4dst);
+
+ tp->key[0] = class << TCAM_V4KEY0_CLASS_CODE_SHIFT;
+ tp->key_mask[0] = TCAM_V4KEY0_CLASS_CODE;
+ tp->key[1] = (u64)l2_rdc_tab << TCAM_V4KEY1_L2RDCNUM_SHIFT;
+ tp->key_mask[1] = TCAM_V4KEY1_L2RDCNUM;
+
+ tp->key[3] = (u64)sip << TCAM_V4KEY3_SADDR_SHIFT;
+ tp->key[3] |= dip;
+
+ tp->key_mask[3] = (u64)sipm << TCAM_V4KEY3_SADDR_SHIFT;
+ tp->key_mask[3] |= dipm;
+
+ tp->key[2] |= ((u64)fsp->h_u.tcp_ip4_spec.tos <<
+ TCAM_V4KEY2_TOS_SHIFT);
+ tp->key_mask[2] |= ((u64)fsp->m_u.tcp_ip4_spec.tos <<
+ TCAM_V4KEY2_TOS_SHIFT);
+ switch (fsp->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ sport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.psrc);
+ spm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.psrc);
+ dport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.pdst);
+ dpm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.pdst);
+
+ tp->key[2] |= (((u64)sport << 16) | dport);
+ tp->key_mask[2] |= (((u64)spm << 16) | dpm);
+ niu_ethflow_to_l3proto(fsp->flow_type, &pid);
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ spi = be32_to_cpu(fsp->h_u.ah_ip4_spec.spi);
+ spim = be32_to_cpu(fsp->m_u.ah_ip4_spec.spi);
+
+ tp->key[2] |= spi;
+ tp->key_mask[2] |= spim;
+ niu_ethflow_to_l3proto(fsp->flow_type, &pid);
+ break;
+ case IP_USER_FLOW:
+ spi = be32_to_cpu(fsp->h_u.usr_ip4_spec.l4_4_bytes);
+ spim = be32_to_cpu(fsp->m_u.usr_ip4_spec.l4_4_bytes);
+
+ tp->key[2] |= spi;
+ tp->key_mask[2] |= spim;
+ pid = fsp->h_u.usr_ip4_spec.proto;
+ break;
+ default:
+ break;
+ }
+
+ tp->key[2] |= ((u64)pid << TCAM_V4KEY2_PROTO_SHIFT);
+ if (pid) {
+ tp->key_mask[2] |= TCAM_V4KEY2_PROTO;
+ }
+}
+
+static int niu_add_ethtool_tcam_entry(struct niu *np,
+ struct ethtool_rxnfc *nfc)
+{
+ struct niu_parent *parent = np->parent;
+ struct niu_tcam_entry *tp;
+ struct ethtool_rx_flow_spec *fsp = &nfc->fs;
+ struct niu_rdc_tables *rdc_table = &parent->rdc_group_cfg[np->port];
+ int l2_rdc_table = rdc_table->first_table_num;
+ u16 idx;
+ u64 class;
+ unsigned long flags;
+ int err, ret;
+
+ ret = 0;
+
+ idx = nfc->fs.location;
+ if (idx >= tcam_get_size(np))
+ return -EINVAL;
+
+ if (fsp->flow_type == IP_USER_FLOW) {
+ int i;
+ int add_usr_cls = 0;
+ int ipv6 = 0;
+ struct ethtool_usrip4_spec *uspec = &fsp->h_u.usr_ip4_spec;
+ struct ethtool_usrip4_spec *umask = &fsp->m_u.usr_ip4_spec;
+
+ niu_lock_parent(np, flags);
+
+ for (i = 0; i < NIU_L3_PROG_CLS; i++) {
+ if (parent->l3_cls[i]) {
+ if (uspec->proto == parent->l3_cls_pid[i]) {
+ class = parent->l3_cls[i];
+ parent->l3_cls_refcnt[i]++;
+ add_usr_cls = 1;
+ break;
+ }
+ } else {
+ /* Program new user IP class */
+ switch (i) {
+ case 0:
+ class = CLASS_CODE_USER_PROG1;
+ break;
+ case 1:
+ class = CLASS_CODE_USER_PROG2;
+ break;
+ case 2:
+ class = CLASS_CODE_USER_PROG3;
+ break;
+ case 3:
+ class = CLASS_CODE_USER_PROG4;
+ break;
+ default:
+ break;
+ }
+ if (uspec->ip_ver == ETH_RX_NFC_IP6)
+ ipv6 = 1;
+ ret = tcam_user_ip_class_set(np, class, ipv6,
+ uspec->proto,
+ uspec->tos,
+ umask->tos);
+ if (ret)
+ goto out;
+
+ ret = tcam_user_ip_class_enable(np, class, 1);
+ if (ret)
+ goto out;
+ parent->l3_cls[i] = class;
+ parent->l3_cls_pid[i] = uspec->proto;
+ parent->l3_cls_refcnt[i]++;
+ add_usr_cls = 1;
+ break;
+ }
+ }
+ if (!add_usr_cls) {
+ pr_info(PFX "niu%d: %s niu_add_ethtool_tcam_entry: "
+ "Could not find/insert class for pid %d\n",
+ parent->index, np->dev->name, uspec->proto);
+ ret = -EINVAL;
+ goto out;
+ }
+ niu_unlock_parent(np, flags);
+ } else {
+ if (!niu_ethflow_to_class(fsp->flow_type, &class)) {
+ return -EINVAL;
+ }
+ }
+
+ niu_lock_parent(np, flags);
+
+ idx = tcam_get_index(np, idx);
+ tp = &parent->tcam[idx];
+
+ memset(tp, 0, sizeof(*tp));
+
+ /* fill in the tcam key and mask */
+ switch (fsp->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table, class);
+ break;
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ /* Not yet implemented */
+ pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
+ "flow %d for IPv6 not implemented\n\n",
+ parent->index, np->dev->name, fsp->flow_type);
+ ret = -EINVAL;
+ goto out;
+ case IP_USER_FLOW:
+ if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4) {
+ niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table,
+ class);
+ } else {
+ /* Not yet implemented */
+ pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
+ "usr flow for IPv6 not implemented\n\n",
+ parent->index, np->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+ default:
+ pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
+ "Unknown flow type %d\n\n",
+ parent->index, np->dev->name, fsp->flow_type);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* fill in the assoc data */
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
+ tp->assoc_data = TCAM_ASSOCDATA_DISC;
+ } else {
+ if (fsp->ring_cookie >= np->num_rx_rings) {
+ pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
+ "Invalid RX ring %lld\n\n",
+ parent->index, np->dev->name,
+ (long long) fsp->ring_cookie);
+ ret = -EINVAL;
+ goto out;
+ }
+ tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET |
+ (fsp->ring_cookie <<
+ TCAM_ASSOCDATA_OFFSET_SHIFT));
+ }
+
+ err = tcam_write(np, idx, tp->key, tp->key_mask);
+ if (err) {
+ ret = -EINVAL;
+ goto out;
+ }
+ err = tcam_assoc_write(np, idx, tp->assoc_data);
+ if (err) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* validate the entry */
+ tp->valid = 1;
+ np->clas.tcam_valid_entries++;
+out:
+ niu_unlock_parent(np, flags);
+
+ return ret;
+}
+
+static int niu_del_ethtool_tcam_entry(struct niu *np, u32 loc)
+{
+ struct niu_parent *parent = np->parent;
+ struct niu_tcam_entry *tp;
+ u16 idx;
+ unsigned long flags;
+ u64 class;
+ int ret = 0;
+
+ if (loc >= tcam_get_size(np))
+ return -EINVAL;
+
+ niu_lock_parent(np, flags);
+
+ idx = tcam_get_index(np, loc);
+ tp = &parent->tcam[idx];
+
+ /* if the entry is of a user defined class, then update*/
+ class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >>
+ TCAM_V4KEY0_CLASS_CODE_SHIFT;
+
+ if (class >= CLASS_CODE_USER_PROG1 && class <= CLASS_CODE_USER_PROG4) {
+ int i;
+ for (i = 0; i < NIU_L3_PROG_CLS; i++) {
+ if (parent->l3_cls[i] == class) {
+ parent->l3_cls_refcnt[i]--;
+ if (!parent->l3_cls_refcnt[i]) {
+ /* disable class */
+ ret = tcam_user_ip_class_enable(np,
+ class,
+ 0);
+ if (ret)
+ goto out;
+ parent->l3_cls[i] = 0;
+ parent->l3_cls_pid[i] = 0;
+ }
+ break;
+ }
+ }
+ if (i == NIU_L3_PROG_CLS) {
+ pr_info(PFX "niu%d: %s In niu_del_ethtool_tcam_entry,"
+ "Usr class 0x%llx not found \n",
+ parent->index, np->dev->name,
+ (unsigned long long) class);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = tcam_flush(np, idx);
+ if (ret)
+ goto out;
+
+ /* invalidate the entry */
+ tp->valid = 0;
+ np->clas.tcam_valid_entries--;
+out:
+ niu_unlock_parent(np, flags);
+
+ return ret;
+}
+
+static int niu_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct niu *np = netdev_priv(dev);
+ int ret = 0;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = niu_set_hash_opts(np, cmd);
+ break;
+ case ETHTOOL_SRXCLSRLINS:
+ ret = niu_add_ethtool_tcam_entry(np, cmd);
+ break;
+ case ETHTOOL_SRXCLSRLDEL:
+ ret = niu_del_ethtool_tcam_entry(np, cmd->fs.location);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct {
const char string[ETH_GSTRING_LEN];
} niu_xmac_stat_keys[] = {
}
}
-static int niu_get_stats_count(struct net_device *dev)
+static int niu_get_sset_count(struct net_device *dev, int stringset)
{
struct niu *np = netdev_priv(dev);
+ if (stringset != ETH_SS_STATS)
+ return -EINVAL;
+
return ((np->flags & NIU_FLAGS_XMAC ?
NUM_XMAC_STAT_KEYS :
NUM_BMAC_STAT_KEYS) +
.get_settings = niu_get_settings,
.set_settings = niu_set_settings,
.get_strings = niu_get_strings,
- .get_stats_count = niu_get_stats_count,
+ .get_sset_count = niu_get_sset_count,
.get_ethtool_stats = niu_get_ethtool_stats,
.phys_id = niu_phys_id,
- .get_rxhash = niu_get_hash_opts,
- .set_rxhash = niu_set_hash_opts,
+ .get_rxnfc = niu_get_nfc,
+ .set_rxnfc = niu_set_nfc,
};
static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,
int i;
for (i = 0; i < len - 5; i++) {
- if (!strncmp(s + i, "FCode ", 5))
+ if (!strncmp(s + i, "FCode ", 6))
break;
}
if (i >= len - 5)
niudbg(PROBE, "niu_classifier_swstate_init: num_tcam(%d)\n",
np->parent->tcam_num_entries);
- cp->tcam_index = (u16) np->port;
+ cp->tcam_top = (u16) np->port;
+ cp->tcam_sz = np->parent->tcam_num_entries / np->parent->num_ports;
cp->h1_init = 0xffffffff;
cp->h2_init = 0xffff;
if (model)
strcpy(np->vpd.model, model);
+ if (of_find_property(dp, "hot-swappable-phy", &prop_len)) {
+ np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER |
+ NIU_FLAGS_HOTPLUG_PHY);
+ }
+
return 0;
#else
return -EINVAL;
plat_dev = platform_device_register_simple("niu", niu_parent_index,
NULL, 0);
- if (!plat_dev)
+ if (IS_ERR(plat_dev))
return NULL;
for (i = 0; attr_name(niu_parent_attributes[i]); i++) {
PCI_EXP_DEVCTL_RELAX_EN);
pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
- dma_mask = DMA_44BIT_MASK;
+ dma_mask = DMA_BIT_MASK(44);
err = pci_set_dma_mask(pdev, dma_mask);
if (!err) {
dev->features |= NETIF_F_HIGHDMA;
goto err_out_release_parent;
}
}
- if (err || dma_mask == DMA_32BIT_MASK) {
- err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err || dma_mask == DMA_BIT_MASK(32)) {
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pdev->dev, PFX "No usable DMA configuration, "
"aborting.\n");
.unmap_single = niu_phys_unmap_single,
};
-static unsigned long res_size(struct resource *r)
-{
- return r->end - r->start + 1UL;
-}
-
static int __devinit niu_of_probe(struct of_device *op,
const struct of_device_id *match)
{
dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM);
np->regs = of_ioremap(&op->resource[1], 0,
- res_size(&op->resource[1]),
+ resource_size(&op->resource[1]),
"niu regs");
if (!np->regs) {
dev_err(&op->dev, PFX "Cannot map device registers, "
}
np->vir_regs_1 = of_ioremap(&op->resource[2], 0,
- res_size(&op->resource[2]),
+ resource_size(&op->resource[2]),
"niu vregs-1");
if (!np->vir_regs_1) {
dev_err(&op->dev, PFX "Cannot map device vir registers 1, "
}
np->vir_regs_2 = of_ioremap(&op->resource[3], 0,
- res_size(&op->resource[3]),
+ resource_size(&op->resource[3]),
"niu vregs-2");
if (!np->vir_regs_2) {
dev_err(&op->dev, PFX "Cannot map device vir registers 2, "
err_out_iounmap:
if (np->vir_regs_1) {
of_iounmap(&op->resource[2], np->vir_regs_1,
- res_size(&op->resource[2]));
+ resource_size(&op->resource[2]));
np->vir_regs_1 = NULL;
}
if (np->vir_regs_2) {
of_iounmap(&op->resource[3], np->vir_regs_2,
- res_size(&op->resource[3]));
+ resource_size(&op->resource[3]));
np->vir_regs_2 = NULL;
}
if (np->regs) {
of_iounmap(&op->resource[1], np->regs,
- res_size(&op->resource[1]));
+ resource_size(&op->resource[1]));
np->regs = NULL;
}
if (np->vir_regs_1) {
of_iounmap(&op->resource[2], np->vir_regs_1,
- res_size(&op->resource[2]));
+ resource_size(&op->resource[2]));
np->vir_regs_1 = NULL;
}
if (np->vir_regs_2) {
of_iounmap(&op->resource[3], np->vir_regs_2,
- res_size(&op->resource[3]));
+ resource_size(&op->resource[3]));
np->vir_regs_2 = NULL;
}
if (np->regs) {
of_iounmap(&op->resource[1], np->regs,
- res_size(&op->resource[1]));
+ resource_size(&op->resource[1]));
np->regs = NULL;
}