driver inclusion category: feature
--------------------------------
Implement the .get_wol and .set_wol callback function.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../motorcomm/yt6801/yt6801_ethtool.c | 169 +++++ .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 576 ++++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 118 ++++ 3 files changed, 863 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c index 12150584f..decef3849 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c @@ -498,6 +498,173 @@ static int fxgmac_set_ringparam(struct net_device *netdev, return ret; }
+static void fxgmac_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC | + WAKE_ARP | WAKE_PHY; + + wol->wolopts = 0; + if (!(pdata->hw_feat.rwk) || !device_can_wakeup(pdata->dev)) { + yt_err(pdata, "%s, pci does not support wakeup.\n", __func__); + return; + } + + wol->wolopts = pdata->wol; +} + +#pragma pack(1) +/* it's better to make this struct's size to 128byte. */ +struct pattern_packet { + u8 ether_daddr[ETH_ALEN]; + u8 ether_saddr[ETH_ALEN]; + u16 ether_type; + + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol */ + u8 ar_hln; /* length of hardware address */ + u8 ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + u8 ar_sha[ETH_ALEN]; /* sender hardware address */ + u8 ar_sip[4]; /* sender IP address */ + u8 ar_tha[ETH_ALEN]; /* target hardware address */ + u8 ar_tip[4]; /* target IP address */ + + u8 reverse[86]; +}; + +#pragma pack() + +static int fxgmac_set_pattern_data(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u8 type_offset, tip_offset, op_offset; + struct wol_bitmap_pattern pattern[4]; + struct pattern_packet packet; + u32 ip_addr, i = 0; + + memset(pattern, 0, sizeof(struct wol_bitmap_pattern) * 4); + + /* Config ucast */ + if (pdata->wol & WAKE_UCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memcpy(pattern[i].pattern_info, pdata->mac_addr, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + /* Config bcast */ + if (pdata->wol & WAKE_BCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memset(pattern[i].pattern_info, 0xFF, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + /* Config mcast */ + if (pdata->wol & WAKE_MCAST) { + pattern[i].mask_info[0] = 0x7; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_info[0] = 0x1; + pattern[i].pattern_info[1] = 0x0; + pattern[i].pattern_info[2] = 0x5E; + pattern[i].pattern_offset = 0; + i++; + } + + /* Config arp */ + if (pdata->wol & WAKE_ARP) { + memset(pattern[i].mask_info, 0, sizeof(pattern[0].mask_info)); + type_offset = offsetof(struct pattern_packet, ar_pro); + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + type_offset++; + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + op_offset = offsetof(struct pattern_packet, ar_op); + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + op_offset++; + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + tip_offset = offsetof(struct pattern_packet, ar_tip); + pattern[i].mask_info[tip_offset / 8] |= 1 << tip_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + + packet.ar_pro = 0x0 << 8 | 0x08; + /* ARP type is 0x0800, notice that ar_pro and ar_op is + * big endian + */ + packet.ar_op = 0x1 << 8; + /* 1 is arp request,2 is arp replay, 3 is rarp request, + * 4 is rarp replay + */ + ip_addr = fxgmac_get_netdev_ip4addr(pdata); + packet.ar_tip[0] = ip_addr & 0xFF; + packet.ar_tip[1] = (ip_addr >> 8) & 0xFF; + packet.ar_tip[2] = (ip_addr >> 16) & 0xFF; + packet.ar_tip[3] = (ip_addr >> 24) & 0xFF; + memcpy(pattern[i].pattern_info, &packet, MAX_PATTERN_SIZE); + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_offset = 0; + i++; + } + + return hw_ops->set_wake_pattern(pdata, pattern, i); +} + +static int fxgmac_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret; + + if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_FILTER)) { + yt_err(pdata, "%s, not supported wol options, 0x%x\n", __func__, + wol->wolopts); + return -EOPNOTSUPP; + } + + if (!(pdata->hw_feat.rwk)) { + yt_err(pdata, "%s, hw wol feature is n/a\n", __func__); + return wol->wolopts ? -EOPNOTSUPP : 0; + } + + pdata->wol = 0; + if (wol->wolopts & WAKE_UCAST) + pdata->wol |= WAKE_UCAST; + + if (wol->wolopts & WAKE_MCAST) + pdata->wol |= WAKE_MCAST; + + if (wol->wolopts & WAKE_BCAST) + pdata->wol |= WAKE_BCAST; + + if (wol->wolopts & WAKE_MAGIC) + pdata->wol |= WAKE_MAGIC; + + if (wol->wolopts & WAKE_PHY) + pdata->wol |= WAKE_PHY; + + if (wol->wolopts & WAKE_ARP) + pdata->wol |= WAKE_ARP; + + ret = fxgmac_set_pattern_data(pdata); + if (ret < 0) + return ret; + + ret = fxgmac_config_wol(pdata, (!!(pdata->wol))); + yt_dbg(pdata, "%s, opt=0x%x, wol=0x%x\n", __func__, wol->wolopts, + pdata->wol); + + return ret; +} + static int fxgmac_get_regs_len(struct net_device __always_unused *netdev) { return FXGMAC_PHY_REG_CNT * sizeof(u32); @@ -656,6 +823,8 @@ static const struct ethtool_ops fxgmac_ethtool_ops = { .set_rxnfc = fxgmac_set_rxnfc, .get_rxfh_indir_size = fxgmac_rss_indir_size, .get_rxfh_key_size = fxgmac_get_rxfh_key_size, + .get_wol = fxgmac_get_wol, + .set_wol = fxgmac_set_wol, .nway_reset = phy_ethtool_nway_reset, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c index a70fa4ede..bd3036625 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c @@ -1399,6 +1399,187 @@ static void fxgmac_config_rss(struct fxgmac_pdata *pdata) fxgmac_disable_rss(pdata); }
+static void fxgmac_update_aoe_ipv4addr(struct fxgmac_pdata *pdata, u8 *ip_addr) +{ + u32 val, ipval = 0; + + if (ip_addr) { + ipval = (ip_addr[0] << 24) | (ip_addr[1] << 16) | + (ip_addr[2] << 8) | (ip_addr[3] << 0); + yt_dbg(pdata, + "%s, covert IP dotted-addr %s to binary 0x%08x ok.\n", + __func__, ip_addr, cpu_to_be32(ipval)); + } else { + /* Get ipv4 addr from net device */ + ipval = fxgmac_get_netdev_ip4addr(pdata); + yt_dbg(pdata, "%s, Get net device binary IP ok, 0x%08x\n", + __func__, cpu_to_be32(ipval)); + + ipval = cpu_to_be32(ipval); + } + + val = rd32_mac(pdata, MAC_ARP_PROTO_ADDR); + if (val != (ipval)) { + wr32_mac(pdata, ipval, MAC_ARP_PROTO_ADDR); + yt_dbg(pdata, + "%s, update arp ipaddr reg from 0x%08x to 0x%08x\n", + __func__, val, ipval); + } +} + +static void fxgmac_enable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 1); + wr32_mac(pdata, val, MAC_CR); +} + +static void fxgmac_disable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 0); + wr32_mac(pdata, val, MAC_CR); +} + +/** + * fxgmac_set_ns_offload - config register for NS offload function + * @pdata: board private structure + * @index: 0~1, index to NS look up table. one entry of the lut is like + * this |remote|solicited|target0|target1| + * @remote_addr: ipv6 addr where yt6801 gets the NS solicitation pkt(request). + * in common, it is 0 to match any remote machine. + * @solicited_addr: the solicited node multicast group address which + * yt6801 computes and joins. + * @target_addr1: it is the target address in NS solicitation pkt. + * @target_addr2:second target address, any address (with ast 6B same with + * target address) + * @mac_addr: it is the target address in NS solicitation pkt. + */ +static void +fxgmac_set_ns_offload(struct fxgmac_pdata *pdata, unsigned int index, + unsigned char *remote_addr, unsigned char *solicited_addr, + unsigned char *target_addr1, unsigned char *target_addr2, + unsigned char *mac_addr) +{ + u32 base = (NS_LUT_MAC_ADDR_CTL - NS_LUT_ROMOTE0 + 4) * index; + u32 address[4], mac_hi, mac_lo; + u8 remote_not_zero = 0; + u32 val; + + val = rd32_mem(pdata, NS_TPID_PRO); + fxgmac_set_bits(&val, NS_TPID_PRO_STPID_POS, NS_TPID_PRO_STPID_LEN, + 0X8100); + fxgmac_set_bits(&val, NS_TPID_PRO_CTPID_POS, NS_TPID_PRO_CTPID_LEN, + 0X9100); + wr32_mem(pdata, val, NS_TPID_PRO); + + val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL); + fxgmac_set_bits(&val, NS_LUT_DST_CMP_TYPE_POS, NS_LUT_DST_CMP_TYPE_LEN, + 1); + fxgmac_set_bits(&val, NS_LUT_DST_IGNORED_POS, NS_LUT_DST_IGNORED_LEN, + 1); + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + fxgmac_set_bits(&val, NS_LUT_TARGET_ISANY_POS, NS_LUT_TARGET_ISANY_LEN, + 0); + wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL); + + for (u32 i = 0; i < 16 / 4; i++) { + address[i] = (remote_addr[i * 4 + 0] << 24) | + (remote_addr[i * 4 + 1] << 16) | + (remote_addr[i * 4 + 2] << 8) | + (remote_addr[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_ROMOTE0 + 4 * i); + if (address[i]) + remote_not_zero = 1; + + address[i] = (target_addr1[i * 4 + 0] << 24) | + (target_addr1[i * 4 + 1] << 16) | + (target_addr1[i * 4 + 2] << 8) | + (target_addr1[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_TARGET0 + 4 * i); + address[i] = (solicited_addr[i * 4 + 0] << 24) | + (solicited_addr[i * 4 + 1] << 16) | + (solicited_addr[i * 4 + 2] << 8) | + (solicited_addr[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_SOLICITED0 + 4 * i); + address[i] = (target_addr2[i * 4 + 0] << 24) | + (target_addr2[i * 4 + 1] << 16) | + (target_addr2[i * 4 + 2] << 8) | + (target_addr2[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], + 0X10 * index + NS_LUT_TARGET4 + 4 * i); + } + mac_hi = (mac_addr[0] << 24) | (mac_addr[1] << 16) | + (mac_addr[2] << 8) | (mac_addr[3] << 0); + mac_lo = (mac_addr[4] << 8) | (mac_addr[5] << 0); + wr32_mem(pdata, mac_hi, base + NS_LUT_MAC_ADDR); + + val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL); + fxgmac_set_bits(&val, NS_LUT_MAC_ADDR_LOW_POS, NS_LUT_MAC_ADDR_LOW_LEN, + mac_lo); + if (remote_not_zero == 0) + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 0); + else + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL); +} + +static void fxgmac_update_ns_offload_ipv6addr(struct fxgmac_pdata *pdata, + unsigned int param) +{ + struct net_device *netdev = pdata->netdev; + unsigned char addr_buf[5][16]; + static u8 ns_offload_tab_idx; + unsigned char *solicited_addr; + unsigned char *target_addr1; + unsigned char *remote_addr; + unsigned char *mac_addr; + + remote_addr = (unsigned char *)&addr_buf[0][0]; + solicited_addr = (unsigned char *)&addr_buf[1][0]; + target_addr1 = (unsigned char *)&addr_buf[2][0]; + mac_addr = (unsigned char *)&addr_buf[4][0]; + + param &= (FXGMAC_NS_IFA_LOCAL_LINK | FXGMAC_NS_IFA_GLOBAL_UNICAST); + /* Get ipv6 addr from net device */ + if (fxgmac_get_netdev_ip6addr(pdata, target_addr1, solicited_addr, + param) == NULL) { + yt_err(pdata, + "%s, get ipv6 addr with err and ignore NS offload.\n", + __func__); + return; + } + + yt_dbg(pdata, "%s, IPv6 local-link=%pI6, solicited =%pI6\n", __func__, + target_addr1, solicited_addr); + + memcpy(mac_addr, netdev->dev_addr, netdev->addr_len); + yt_dbg(pdata, "ns_tab idx=%d, %02x:%02x:%02x:%02x:%02x:%02x\n", + ns_offload_tab_idx, mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + memset(remote_addr, 0, 16); + fxgmac_set_ns_offload(pdata, ns_offload_tab_idx++, remote_addr, + solicited_addr, target_addr1, target_addr1, + mac_addr); + +#define NS_OFFLOAD_TAB_MAX_IDX 2 + if (ns_offload_tab_idx >= NS_OFFLOAD_TAB_MAX_IDX) + ns_offload_tab_idx = 0; +} + +static void fxgmac_enable_ns_offload(struct fxgmac_pdata *pdata) +{ + wr32_mem(pdata, 0X00000011, NS_OF_GLB_CTL); +} + static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata) { u32 val; @@ -1419,6 +1600,390 @@ static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata) return 0; }
+static int fxgmac_set_wake_pattern_mask(struct fxgmac_pdata *pdata, + u32 filter_index, u8 register_index, + u32 data) +{ + const u16 address_offset[16][3] = { + {0x1020, 0x1024, 0x1028}, + {0x102c, 0x1030, 0x1034}, + {0x1038, 0x103c, 0x1040}, + {0x1044, 0x1050, 0x1054}, + {0x1058, 0x105c, 0x1060}, + {0x1064, 0x1068, 0x106c}, + {0x1070, 0x1074, 0x1078}, + {0x107c, 0x1080, 0x1084}, + {0x1088, 0x108c, 0x1090}, + {0x1134, 0x113c, 0x1140}, + {0x1208, 0x1200, 0x1204}, + {0x1218, 0x1210, 0x1214}, + {0x1228, 0x1220, 0x1224}, + {0x1238, 0x1230, 0x1234}, + {0x1248, 0x1240, 0x1244}, + {0x1258, 0x1250, 0x1254}, + }; + if (filter_index > 15 || register_index > 2) { + yt_err(pdata, + "%s, xx is over range, filter:%d, register:0x%x\n", + __func__, filter_index, register_index); + return -EINVAL; + } + wr32_mem(pdata, data, address_offset[filter_index][register_index]); + return 0; +} + +union type16 { + u16 raw; + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + u16 bit_8 : 1; + u16 bit_9 : 1; + u16 bit_10 : 1; + u16 bit_11 : 1; + u16 bit_12 : 1; + u16 bit_13 : 1; + u16 bit_14 : 1; + u16 bit_15 : 1; + } bits; +}; + +union type8 { + u16 raw; + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + } bits; +}; + +static u16 wol_crc16(u8 *frame, u16 uslen) +{ + union type16 crc, crc_comb; + union type8 next_crc, rrpe_data; + + next_crc.raw = 0; + crc.raw = 0xffff; + for (u32 i = 0; i < uslen; i++) { + rrpe_data.raw = frame[i]; + next_crc.bits.bit_0 = crc.bits.bit_15 ^ rrpe_data.bits.bit_0; + next_crc.bits.bit_1 = crc.bits.bit_14 ^ next_crc.bits.bit_0 ^ + rrpe_data.bits.bit_1; + next_crc.bits.bit_2 = crc.bits.bit_13 ^ next_crc.bits.bit_1 ^ + rrpe_data.bits.bit_2; + next_crc.bits.bit_3 = crc.bits.bit_12 ^ next_crc.bits.bit_2 ^ + rrpe_data.bits.bit_3; + next_crc.bits.bit_4 = crc.bits.bit_11 ^ next_crc.bits.bit_3 ^ + rrpe_data.bits.bit_4; + next_crc.bits.bit_5 = crc.bits.bit_10 ^ next_crc.bits.bit_4 ^ + rrpe_data.bits.bit_5; + next_crc.bits.bit_6 = crc.bits.bit_9 ^ next_crc.bits.bit_5 ^ + rrpe_data.bits.bit_6; + next_crc.bits.bit_7 = crc.bits.bit_8 ^ next_crc.bits.bit_6 ^ + rrpe_data.bits.bit_7; + + crc_comb.bits.bit_15 = crc.bits.bit_7 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_14 = crc.bits.bit_6; + crc_comb.bits.bit_13 = crc.bits.bit_5; + crc_comb.bits.bit_12 = crc.bits.bit_4; + crc_comb.bits.bit_11 = crc.bits.bit_3; + crc_comb.bits.bit_10 = crc.bits.bit_2; + crc_comb.bits.bit_9 = crc.bits.bit_1 ^ next_crc.bits.bit_0; + crc_comb.bits.bit_8 = crc.bits.bit_0 ^ next_crc.bits.bit_1; + crc_comb.bits.bit_7 = next_crc.bits.bit_0 ^ next_crc.bits.bit_2; + crc_comb.bits.bit_6 = next_crc.bits.bit_1 ^ next_crc.bits.bit_3; + crc_comb.bits.bit_5 = next_crc.bits.bit_2 ^ next_crc.bits.bit_4; + crc_comb.bits.bit_4 = next_crc.bits.bit_3 ^ next_crc.bits.bit_5; + crc_comb.bits.bit_3 = next_crc.bits.bit_4 ^ next_crc.bits.bit_6; + crc_comb.bits.bit_2 = next_crc.bits.bit_5 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_1 = next_crc.bits.bit_6; + crc_comb.bits.bit_0 = next_crc.bits.bit_7; + crc.raw = crc_comb.raw; + } + return crc.raw; +} + +static void __write_filter(struct fxgmac_pdata *pdata, u8 i, u8 n) +{ + u8 *mask = &pdata->pattern[i * 4 + n].mask_info[0]; + u32 val; + + val = (mask[3] & 0x7f << 24) | (mask[2] << 16) | (mask[1] << 8) | + (mask[0] << 0); + + wr32_mac(pdata, val, MAC_RWK_PAC); +} + +static u32 __set_filter_addr_type(struct fxgmac_pdata *pdata, u8 i, u8 n, + u32 total_cnt, u32 pattern_cnt) +{ + struct wol_bitmap_pattern *pattern = &pdata->pattern[i * 4 + n]; + u32 val; + + /* Set filter enable bit. */ + val = ((i * 4 + n) < total_cnt) ? (0x1 << 8 * n) : 0x0; + + /* Set filter address type, 0- unicast, 1 - multicast. */ + val |= (i * 4 + n >= total_cnt) ? 0x0 : + (i * 4 + n >= pattern_cnt) ? (0x1 << (3 + 8 * n)) : + pattern->pattern_offset ? 0x0 : + !(pattern->mask_info[0] & 0x01) ? 0x0 : + (pattern->pattern_info[0] & 0x01) ? (0x1 << (3 + 8 * n)) : + 0x0; + return val; +} + +static u32 __wake_pattern_mask_val(struct wol_bitmap_pattern *pattern, u8 n) +{ + u8 *mask = &pattern->mask_info[n * 4]; + u32 val; + + val = ((mask[7] & 0x7f) << (24 + 1)) | (mask[6] << (16 + 1)) | + (mask[5] << (8 + 1)) | (mask[4] << (0 + 1)) | + ((mask[3] & 0x80) >> 7); + + return val; +} + +static int fxgmac_set_wake_pattern(struct fxgmac_pdata *pdata, + struct wol_bitmap_pattern *wol_pattern, + u32 pattern_cnt) +{ + u32 total_cnt = 0, inherited_cnt = 0, val; + struct wol_bitmap_pattern *pattern; + u16 map_index, mask_index; + u8 mask[MAX_PATTERN_SIZE]; + u32 i, j, z; + + if (pattern_cnt > MAX_PATTERN_COUNT) { + yt_err(pdata, "%s, %d patterns exceed %d, not supported!\n", + __func__, pattern_cnt, MAX_PATTERN_COUNT); + return -EINVAL; + } + + /* Reset the FIFO head pointer. */ + if (fxgmac_check_wake_pattern_fifo_pointer(pdata)) { + yt_err(pdata, "%s, remote pattern array pointer is not be 0\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < pattern_cnt; i++) { + pattern = &pdata->pattern[i]; + memcpy(pattern, wol_pattern + i, sizeof(wol_pattern[0])); + + if (pattern_cnt + inherited_cnt >= MAX_PATTERN_COUNT) + continue; + + if (wol_pattern[i].pattern_offset || + !(wol_pattern[i].mask_info[0] & 0x01)) { + pattern = &pdata->pattern[pattern_cnt + inherited_cnt]; + memcpy(pattern, wol_pattern + i, + sizeof(wol_pattern[0])); + inherited_cnt++; + } + } + total_cnt = pattern_cnt + inherited_cnt; + + /* Calculate the crc-16 of the mask pattern */ + for (i = 0; i < total_cnt; i++) { + /* Please program pattern[i] to NIC for pattern match wakeup. + * pattern_size, pattern_info, mask_info + */ + mask_index = 0; + map_index = 0; + pattern = &pdata->pattern[i]; + for (j = 0; j < pattern->mask_size; j++) { + for (z = 0; + z < ((j == (MAX_PATTERN_SIZE / 8 - 1)) ? 7 : 8); + z++) { + if (pattern->mask_info[j] & (0x01 << z)) { + mask[map_index] = + pattern->pattern_info + [pattern->pattern_offset + + mask_index]; + map_index++; + } + mask_index++; + } + } + /* Calculate the crc-16 of the mask pattern */ + pattern->pattern_crc = wol_crc16(mask, map_index); + memset(mask, 0, sizeof(mask)); + } + + /* Write patterns by FIFO block. */ + for (i = 0; i < (total_cnt + 3) / 4; i++) { + /* 1. Write the first 4Bytes of Filter. */ + __write_filter(pdata, i, 0); + __write_filter(pdata, i, 1); + __write_filter(pdata, i, 2); + __write_filter(pdata, i, 3); + + /* 2. Write the Filter Command. */ + val = 0; + val |= __set_filter_addr_type(pdata, i, 0, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 1, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 2, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 3, total_cnt, + pattern_cnt); + wr32_mac(pdata, val, MAC_RWK_PAC); + + /* 3. Write the mask offset. */ + val = (pdata->pattern[i * 4 + 3].pattern_offset << 24) | + (pdata->pattern[i * 4 + 2].pattern_offset << 16) | + (pdata->pattern[i * 4 + 1].pattern_offset << 8) | + (pdata->pattern[i * 4 + 0].pattern_offset << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + + /* 4. Write the masked data CRC. */ + val = (pdata->pattern[i * 4 + 1].pattern_crc << 16) | + (pdata->pattern[i * 4 + 0].pattern_crc << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + val = (pdata->pattern[i * 4 + 3].pattern_crc << 16) | + (pdata->pattern[i * 4 + 2].pattern_crc << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + } + + for (i = 0; i < total_cnt; i++) { + /* Global manager regitster mask bit 31~62 */ + pattern = &pdata->pattern[i]; + val = __wake_pattern_mask_val(pattern, 0); + fxgmac_set_wake_pattern_mask(pdata, i, 0, val); + + /* Global manager regitster mask bit 63~94 */ + val = __wake_pattern_mask_val(pattern, 1); + fxgmac_set_wake_pattern_mask(pdata, i, 1, val); + + /* Global manager regitster mask bit 95~126 */ + val = __wake_pattern_mask_val(pattern, 2); + fxgmac_set_wake_pattern_mask(pdata, i, 2, val); + } + + return 0; +} + +static void fxgmac_enable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_disable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 0); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_enable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + struct pci_dev *pdev = to_pci_dev(pdata->dev); + u16 pm_ctrl; + u32 val; + int pos; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); + + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (!pos) { + yt_err(pdata, "Unable to find Power Management Capabilities\n"); + return; + } + + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pm_ctrl); + pm_ctrl |= PCI_PM_CTRL_PME_ENABLE; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pm_ctrl); +} + +static void fxgmac_disable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 0); + wr32_mac(pdata, val, MAC_PMT_STA); +} + +static void fxgmac_enable_wake_packet_indication(struct fxgmac_pdata *pdata, + int en) +{ + u32 val_wpi_crtl0; + + rd32_mem(pdata, MGMT_WOL_CTRL); /* Read-clear WoL event. */ + + /* Prepare to write packet data by write wpi_mode to 1 */ + val_wpi_crtl0 = rd32_mem(pdata, MGMT_WPI_CTRL0); + fxgmac_set_bits(&val_wpi_crtl0, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, + (en ? MGMT_WPI_CTRL0_WPI_MODE_WR : + MGMT_WPI_CTRL0_WPI_MODE_NORMAL)); + wr32_mem(pdata, val_wpi_crtl0, MGMT_WPI_CTRL0); +} + +static void fxgmac_enable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_disable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); +} + static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata) { struct fxgmac_channel *channel = pdata->channel_head; @@ -2686,4 +3251,15 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops) hw_ops->set_rss_options = fxgmac_write_rss_options; hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key; hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table; + + /* Wake */ + hw_ops->disable_arp_offload = fxgmac_disable_arp_offload; + hw_ops->enable_wake_magic_pattern = fxgmac_enable_wake_magic_pattern; + hw_ops->disable_wake_magic_pattern = fxgmac_disable_wake_magic_pattern; + hw_ops->enable_wake_link_change = fxgmac_enable_wake_link_change; + hw_ops->disable_wake_link_change = fxgmac_disable_wake_link_change; + hw_ops->set_wake_pattern = fxgmac_set_wake_pattern; + hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern; + hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern; + } diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index c5c13601e..6a3d1073c 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -14,6 +14,89 @@ const struct net_device_ops *fxgmac_get_netdev_ops(void); static void fxgmac_napi_enable(struct fxgmac_pdata *pdata);
+unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata) +{ + unsigned int ipval = 0xc0a801ca; /* 192.168.1.202 */ + struct net_device *netdev = pdata->netdev; + struct in_ifaddr *ifa; + + rcu_read_lock(); + + /* We only get the first IPv4 addr. */ + ifa = rcu_dereference(netdev->ip_ptr->ifa_list); + if (ifa) { + ipval = (unsigned int)ifa->ifa_address; + yt_dbg(pdata, "%s, netdev %s IPv4 address %pI4, mask: %pI4\n", + __func__, ifa->ifa_label, &ifa->ifa_address, + &ifa->ifa_mask); + } + + rcu_read_unlock(); + + return ipval; +} + +unsigned char *fxgmac_get_netdev_ip6addr(struct fxgmac_pdata *pdata, + unsigned char *ipval, + unsigned char *ip6addr_solicited, + unsigned int ifa_flag) +{ + struct net_device *netdev = pdata->netdev; + unsigned char solicited_ipval[16] = { 0 }; + unsigned char local_ipval[16] = { 0 }; + struct in6_addr *addr_ip6_solicited; + struct in6_addr *addr_ip6; + struct inet6_ifaddr *ifp; + int err = -EADDRNOTAVAIL; + struct inet6_dev *i6dev; + + if (!(ifa_flag & + (FXGMAC_NS_IFA_GLOBAL_UNICAST | FXGMAC_NS_IFA_LOCAL_LINK))) { + yt_err(pdata, "%s, ifa_flag :%d is err.\n", __func__, ifa_flag); + return NULL; + } + + addr_ip6 = (struct in6_addr *)local_ipval; + addr_ip6_solicited = (struct in6_addr *)solicited_ipval; + + if (ipval) + addr_ip6 = (struct in6_addr *)ipval; + + if (ip6addr_solicited) + addr_ip6_solicited = (struct in6_addr *)ip6addr_solicited; + + in6_pton("fe80::4808:8ffb:d93e:d753", -1, (u8 *)addr_ip6, -1, NULL); + + rcu_read_lock(); + i6dev = __in6_dev_get(netdev); + if (!i6dev) + goto err; + + read_lock_bh(&i6dev->lock); + list_for_each_entry(ifp, &i6dev->addr_list, if_list) { + if (((ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) && + ifp->scope != IFA_LINK) || + ((ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) && + ifp->scope == IFA_LINK)) { + memcpy(addr_ip6, &ifp->addr, 16); + addrconf_addr_solict_mult(addr_ip6, addr_ip6_solicited); + err = 0; + break; + } + } + read_unlock_bh(&i6dev->lock); + + if (err) + goto err; + + rcu_read_unlock(); + return ipval; +err: + rcu_read_unlock(); + yt_err(pdata, "%s, get ipv6 addr err, use default.\n", __func__); + return NULL; +} + static unsigned int fxgmac_desc_tx_avail(struct fxgmac_ring *ring) { unsigned int avail; @@ -903,6 +986,41 @@ static void fxgmac_restart_work(struct work_struct *work) rtnl_unlock(); }
+int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (!pdata->hw_feat.rwk) { + yt_err(pdata, "error configuring WOL - not supported.\n"); + return -EOPNOTSUPP; + } + + hw_ops->disable_wake_magic_pattern(pdata); + hw_ops->disable_wake_pattern(pdata); + hw_ops->disable_wake_link_change(pdata); + + if (en) { + /* Config mac address for rx of magic or ucast */ + hw_ops->set_mac_address(pdata, (u8 *)(pdata->netdev->dev_addr)); + + /* Enable Magic packet */ + if (pdata->wol & WAKE_MAGIC) + hw_ops->enable_wake_magic_pattern(pdata); + + /* Enable global unicast packet */ + if (pdata->wol & + (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP)) + hw_ops->enable_wake_pattern(pdata); + + /* Enable ephy link change */ + if (pdata->wol & WAKE_PHY) + hw_ops->enable_wake_link_change(pdata); + } + device_set_wakeup_enable((pdata->dev), en); + + return 0; +} + int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;