📄 niu.c
字号:
struct net_device *dev = np->dev; unsigned long flags; if (!netif_carrier_ok(dev) && link_up) { niuinfo(LINK, "%s: Link is up at %s, %s duplex\n", dev->name, (lp->active_speed == SPEED_10000 ? "10Gb/sec" : (lp->active_speed == SPEED_1000 ? "1Gb/sec" : (lp->active_speed == SPEED_100 ? "100Mbit/sec" : "10Mbit/sec"))), (lp->active_duplex == DUPLEX_FULL ? "full" : "half")); spin_lock_irqsave(&np->lock, flags); niu_init_xif(np); niu_handle_led(np, 1); spin_unlock_irqrestore(&np->lock, flags); netif_carrier_on(dev); } else if (netif_carrier_ok(dev) && !link_up) { niuwarn(LINK, "%s: Link is down\n", dev->name); spin_lock_irqsave(&np->lock, flags); niu_handle_led(np, 0); spin_unlock_irqrestore(&np->lock, flags); netif_carrier_off(dev); } return 0;}static int link_status_10g_mrvl(struct niu *np, int *link_up_p){ int err, link_up, pma_status, pcs_status; link_up = 0; err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, MRVL88X2011_10G_PMD_STATUS_2); if (err < 0) goto out; /* Check PMA/PMD Register: 1.0001.2 == 1 */ err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, MRVL88X2011_PMA_PMD_STATUS_1); if (err < 0) goto out; pma_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0); /* Check PMC Register : 3.0001.2 == 1: read twice */ err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, MRVL88X2011_PMA_PMD_STATUS_1); if (err < 0) goto out; err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, MRVL88X2011_PMA_PMD_STATUS_1); if (err < 0) goto out; pcs_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0); /* Check XGXS Register : 4.0018.[0-3,12] */ err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV4_ADDR, MRVL88X2011_10G_XGXS_LANE_STAT); if (err < 0) goto out; if (err == (PHYXS_XGXS_LANE_STAT_ALINGED | PHYXS_XGXS_LANE_STAT_LANE3 | PHYXS_XGXS_LANE_STAT_LANE2 | PHYXS_XGXS_LANE_STAT_LANE1 | PHYXS_XGXS_LANE_STAT_LANE0 | PHYXS_XGXS_LANE_STAT_MAGIC | 0x800)) link_up = (pma_status && pcs_status) ? 1 : 0; np->link_config.active_speed = SPEED_10000; np->link_config.active_duplex = DUPLEX_FULL; err = 0;out: mrvl88x2011_act_led(np, (link_up ? MRVL88X2011_LED_CTL_PCS_ACT : MRVL88X2011_LED_CTL_OFF)); *link_up_p = link_up; return err;}static int link_status_10g_bcom(struct niu *np, int *link_up_p){ int err, link_up; link_up = 0; err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, BCM8704_PMD_RCV_SIGDET); if (err < 0) goto out; if (!(err & PMD_RCV_SIGDET_GLOBAL)) { err = 0; goto out; } err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, BCM8704_PCS_10G_R_STATUS); if (err < 0) goto out; if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) { err = 0; goto out; } err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, BCM8704_PHYXS_XGXS_LANE_STAT); if (err < 0) goto out; if (err != (PHYXS_XGXS_LANE_STAT_ALINGED | PHYXS_XGXS_LANE_STAT_MAGIC | PHYXS_XGXS_LANE_STAT_LANE3 | PHYXS_XGXS_LANE_STAT_LANE2 | PHYXS_XGXS_LANE_STAT_LANE1 | PHYXS_XGXS_LANE_STAT_LANE0)) { err = 0; goto out; } link_up = 1; np->link_config.active_speed = SPEED_10000; np->link_config.active_duplex = DUPLEX_FULL; err = 0;out: *link_up_p = link_up; return err;}static int link_status_10g(struct niu *np, int *link_up_p){ unsigned long flags; int err = -EINVAL; spin_lock_irqsave(&np->lock, flags); if (np->link_config.loopback_mode == LOOPBACK_DISABLED) { int phy_id; phy_id = phy_decode(np->parent->port_phy, np->port); phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port]; /* handle different phy types */ switch (phy_id & NIU_PHY_ID_MASK) { case NIU_PHY_ID_MRVL88X2011: err = link_status_10g_mrvl(np, link_up_p); break; default: /* bcom 8704 */ err = link_status_10g_bcom(np, link_up_p); break; } } spin_unlock_irqrestore(&np->lock, flags); return err;}static int link_status_1g(struct niu *np, int *link_up_p){ struct niu_link_config *lp = &np->link_config; u16 current_speed, bmsr; unsigned long flags; u8 current_duplex; int err, link_up; link_up = 0; current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; spin_lock_irqsave(&np->lock, flags); err = -EINVAL; if (np->link_config.loopback_mode != LOOPBACK_DISABLED) goto out; err = mii_read(np, np->phy_addr, MII_BMSR); if (err < 0) goto out; bmsr = err; if (bmsr & BMSR_LSTATUS) { u16 adv, lpa, common, estat; err = mii_read(np, np->phy_addr, MII_ADVERTISE); if (err < 0) goto out; adv = err; err = mii_read(np, np->phy_addr, MII_LPA); if (err < 0) goto out; lpa = err; common = adv & lpa; err = mii_read(np, np->phy_addr, MII_ESTATUS); if (err < 0) goto out; estat = err; link_up = 1; if (estat & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) { current_speed = SPEED_1000; if (estat & ESTATUS_1000_TFULL) current_duplex = DUPLEX_FULL; else current_duplex = DUPLEX_HALF; } else { if (common & ADVERTISE_100BASE4) { current_speed = SPEED_100; current_duplex = DUPLEX_HALF; } else if (common & ADVERTISE_100FULL) { current_speed = SPEED_100; current_duplex = DUPLEX_FULL; } else if (common & ADVERTISE_100HALF) { current_speed = SPEED_100; current_duplex = DUPLEX_HALF; } else if (common & ADVERTISE_10FULL) { current_speed = SPEED_10; current_duplex = DUPLEX_FULL; } else if (common & ADVERTISE_10HALF) { current_speed = SPEED_10; current_duplex = DUPLEX_HALF; } else link_up = 0; } } lp->active_speed = current_speed; lp->active_duplex = current_duplex; err = 0;out: spin_unlock_irqrestore(&np->lock, flags); *link_up_p = link_up; return err;}static int niu_link_status(struct niu *np, int *link_up_p){ const struct niu_phy_ops *ops = np->phy_ops; int err; err = 0; if (ops->link_status) err = ops->link_status(np, link_up_p); return err;}static void niu_timer(unsigned long __opaque){ struct niu *np = (struct niu *) __opaque; unsigned long off; int err, link_up; err = niu_link_status(np, &link_up); if (!err) niu_link_status_common(np, link_up); if (netif_carrier_ok(np->dev)) off = 5 * HZ; else off = 1 * HZ; np->timer.expires = jiffies + off; add_timer(&np->timer);}static const struct niu_phy_ops phy_ops_10g_fiber_niu = { .serdes_init = serdes_init_niu, .xcvr_init = xcvr_init_10g, .link_status = link_status_10g,};static const struct niu_phy_ops phy_ops_10g_fiber = { .serdes_init = serdes_init_10g, .xcvr_init = xcvr_init_10g, .link_status = link_status_10g,};static const struct niu_phy_ops phy_ops_10g_copper = { .serdes_init = serdes_init_10g, .link_status = link_status_10g, /* XXX */};static const struct niu_phy_ops phy_ops_1g_fiber = { .serdes_init = serdes_init_1g, .xcvr_init = xcvr_init_1g, .link_status = link_status_1g,};static const struct niu_phy_ops phy_ops_1g_copper = { .xcvr_init = xcvr_init_1g, .link_status = link_status_1g,};struct niu_phy_template { const struct niu_phy_ops *ops; u32 phy_addr_base;};static const struct niu_phy_template phy_template_niu = { .ops = &phy_ops_10g_fiber_niu, .phy_addr_base = 16,};static const struct niu_phy_template phy_template_10g_fiber = { .ops = &phy_ops_10g_fiber, .phy_addr_base = 8,};static const struct niu_phy_template phy_template_10g_copper = { .ops = &phy_ops_10g_copper, .phy_addr_base = 10,};static const struct niu_phy_template phy_template_1g_fiber = { .ops = &phy_ops_1g_fiber, .phy_addr_base = 0,};static const struct niu_phy_template phy_template_1g_copper = { .ops = &phy_ops_1g_copper, .phy_addr_base = 0,};static int niu_determine_phy_disposition(struct niu *np){ struct niu_parent *parent = np->parent; u8 plat_type = parent->plat_type; const struct niu_phy_template *tp; u32 phy_addr_off = 0; if (plat_type == PLAT_TYPE_NIU) { tp = &phy_template_niu; phy_addr_off += np->port; } else { switch (np->flags & (NIU_FLAGS_10G | NIU_FLAGS_FIBER)) { case 0: /* 1G copper */ tp = &phy_template_1g_copper; if (plat_type == PLAT_TYPE_VF_P0) phy_addr_off = 10; else if (plat_type == PLAT_TYPE_VF_P1) phy_addr_off = 26; phy_addr_off += (np->port ^ 0x3); break; case NIU_FLAGS_10G: /* 10G copper */ tp = &phy_template_1g_copper; break; case NIU_FLAGS_FIBER: /* 1G fiber */ tp = &phy_template_1g_fiber; break; case NIU_FLAGS_10G | NIU_FLAGS_FIBER: /* 10G fiber */ tp = &phy_template_10g_fiber; if (plat_type == PLAT_TYPE_VF_P0 || plat_type == PLAT_TYPE_VF_P1) phy_addr_off = 8; phy_addr_off += np->port; break; default: return -EINVAL; } } np->phy_ops = tp->ops; np->phy_addr = tp->phy_addr_base + phy_addr_off; return 0;}static int niu_init_link(struct niu *np){ struct niu_parent *parent = np->parent; int err, ignore; if (parent->plat_type == PLAT_TYPE_NIU) { err = niu_xcvr_init(np); if (err) return err; msleep(200); } err = niu_serdes_init(np); if (err) return err; msleep(200); err = niu_xcvr_init(np); if (!err) niu_link_status(np, &ignore); return 0;}static void niu_set_primary_mac(struct niu *np, unsigned char *addr){ u16 reg0 = addr[4] << 8 | addr[5]; u16 reg1 = addr[2] << 8 | addr[3]; u16 reg2 = addr[0] << 8 | addr[1]; if (np->flags & NIU_FLAGS_XMAC) { nw64_mac(XMAC_ADDR0, reg0); nw64_mac(XMAC_ADDR1, reg1); nw64_mac(XMAC_ADDR2, reg2); } else { nw64_mac(BMAC_ADDR0, reg0); nw64_mac(BMAC_ADDR1, reg1); nw64_mac(BMAC_ADDR2, reg2); }}static int niu_num_alt_addr(struct niu *np){ if (np->flags & NIU_FLAGS_XMAC) return XMAC_NUM_ALT_ADDR; else return BMAC_NUM_ALT_ADDR;}static int niu_set_alt_mac(struct niu *np, int index, unsigned char *addr){ u16 reg0 = addr[4] << 8 | addr[5]; u16 reg1 = addr[2] << 8 | addr[3]; u16 reg2 = addr[0] << 8 | addr[1]; if (index >= niu_num_alt_addr(np)) return -EINVAL; if (np->flags & NIU_FLAGS_XMAC) { nw64_mac(XMAC_ALT_ADDR0(index), reg0); nw64_mac(XMAC_ALT_ADDR1(index), reg1); nw64_mac(XMAC_ALT_ADDR2(index), reg2); } else { nw64_mac(BMAC_ALT_ADDR0(index), reg0); nw64_mac(BMAC_ALT_ADDR1(index), reg1); nw64_mac(BMAC_ALT_ADDR2(index), reg2); } return 0;}static int niu_enable_alt_mac(struct niu *np, int index, int on){ unsigned long reg; u64 val, mask; if (index >= niu_num_alt_addr(np)) return -EINVAL; if (np->flags & NIU_FLAGS_XMAC) reg = XMAC_ADDR_CMPEN; else reg = BMAC_ADDR_CMPEN; mask = 1 << index; val = nr64_mac(reg); if (on) val |= mask; else val &= ~mask; nw64_mac(reg, val); return 0;}static void __set_rdc_table_num_hw(struct niu *np, unsigned long reg, int num, int mac_pref){ u64 val = nr64_mac(reg); val &= ~(HOST_INFO_MACRDCTBLN | HOST_INFO_MPR); val |= num; if (mac_pref) val |= HOST_INFO_MPR; nw64_mac(reg, val);}static int __set_rdc_table_num(struct niu *np, int xmac_index, int bmac_index, int rdc_table_num, int mac_pref){ unsigned long reg; if (rdc_table_num & ~HOST_INFO_MACRDCTBLN) return -EINVAL; if (np->flags & NIU_FLAGS_XMAC) reg = XMAC_HOST_INFO(xmac_index); else reg = BMAC_HOST_INFO(bmac_index); __set_rdc_table_num_hw(np, reg, rdc_table_num, mac_pref); return 0;}static int niu_set_primary_mac_rdc_table(struct niu *np, int table_num, int mac_pref){ return __set_rdc_table_num(np, 17, 0, table_num, mac_pref);}static int niu_set_multicast_mac_rdc_table(struct niu *np, int table_num, int mac_pref){ return __set_rdc_table_num(np, 16, 8, table_num, mac_pref);}static int niu_set_alt_mac_rdc_table(struct niu *np, int idx, int table_num, int mac_pref){ if (idx >= niu_num_alt_addr(np)) return -EINVAL; return __set_rdc_table_num(np, idx, idx + 1, table_num, mac_pref);}static u64 vlan_entry_set_parity(u64 reg_val){ u64 port01_mask; u64 port23_mask; port01_mask = 0x00ff; port23_mask = 0xff00; if (hweight64(reg_val & port01_mask) & 1) reg_val |= ENET_VLAN_TBL_PARITY0; else reg_val &= ~ENET_VLAN_TBL_PARITY0; if (hweight64(reg_val & port23_mask) & 1) reg_val |= ENET_VLAN_TBL_PARITY1; else reg_val &= ~ENET_VLAN_TBL_PARITY1; return reg_val;}static void vlan_tbl_write(struct niu *np, unsigned long index, int port, int vpr, int rdc_table){ u64 reg_val = nr64(ENET_VLAN_TBL(index)); reg_val &= ~((ENET_VLAN_TBL_VPR | ENET_VLAN_TBL_VLANRDCTBLN) << ENET_VLAN_TBL_SHIFT(port)); if (vpr) reg_val |= (ENET_VLAN_TBL_VPR << ENET_VLAN_TBL_SHIFT(port)); reg_val |= (rdc_table << ENET_VLAN_TBL_SHIFT(port)); reg_val = vlan_entry_set_parity(reg_val); nw64(ENET_VLAN_TBL(index), reg_val);}static void vlan_tbl_clear(struct niu *np){ int i; for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++) nw64(ENET_VLAN_TBL(i), 0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -