2367 lines
79 KiB
C
Executable File

/*++
Copyright (c) 2021 Motor-comm Corporation.
Confidential and Proprietary. All rights reserved.
This is Motor-comm Corporation NIC driver relevant files. Please don't copy, modify,
distribute without commercial permission.
--*/
#include <linux/netdevice.h>
#include <linux/tcp.h>
#include <linux/interrupt.h>
#include <linux/inetdevice.h>
#include <linux/inet.h>
#include <net/addrconf.h>
#include "fuxi-gmac.h"
#include "fuxi-gmac-reg.h"
static int fxgmac_one_poll_rx(struct napi_struct *, int);
static int fxgmac_one_poll_tx(struct napi_struct *, int);
static int fxgmac_all_poll(struct napi_struct *, int);
unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata)
{
struct net_device *netdev = pdata->netdev;
struct in_ifaddr *ifa;
unsigned int ipval = 0xc0a801ca; //here just hard code to 192.168.1.202
rcu_read_lock();
/* we only get the first IPv4 addr. */
ifa = rcu_dereference(netdev->ip_ptr->ifa_list);
if(ifa) {
/* binary ipv4 addr with __be */
ipval = (unsigned int)ifa->ifa_address;
DPRINTK("%s, netdev %s IPv4 address %pI4, mask: %pI4\n",__FUNCTION__, ifa->ifa_label, &ifa->ifa_address, &ifa->ifa_mask);
}
// ifa = rcu_dereference(ifa->ifa_next); // more ipv4 addr
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;
struct inet6_dev *i6dev;
struct inet6_ifaddr *ifp;
unsigned char local_ipval[16] = {0};
unsigned char solicited_ipval[16] = {0};
struct in6_addr *addr_ip6 = (struct in6_addr *)local_ipval;
struct in6_addr *addr_ip6_solicited = (struct in6_addr *)solicited_ipval;
int err = -EADDRNOTAVAIL;
//in6_pton("fe80::35c6:dd1b:9745:fc9b", -1, (u8*)ipval, -1, NULL); //here just hard code for default
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); //here just hard code for default
if (ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST)
DPRINTK ("%s FXGMAC_NS_IFA_GLOBAL_UNICAST is set, %x\n", __FUNCTION__, ifa_flag);
if (ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK)
DPRINTK ("%s FXGMAC_NS_IFA_LOCAL_LINK is set, %x\n", __FUNCTION__, ifa_flag);
rcu_read_lock();
i6dev = __in6_dev_get(netdev);
if (i6dev != NULL) {
read_lock_bh(&i6dev->lock);
list_for_each_entry(ifp, &i6dev->addr_list, if_list) {
/* here we need only the ll addr, use scope to filter out it. */
if (((ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) && (ifp->scope != IFA_LINK)) || ((ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) && (ifp->scope == IFA_LINK)/* &&
!(ifp->flags & IFA_F_TENTATIVE)*/)) {
memcpy(addr_ip6, &ifp->addr, 16);
addrconf_addr_solict_mult(addr_ip6, addr_ip6_solicited);
err = 0;
//DPRINTK("%s, netdev %s IPv6 local-link address %pI6\n",__FUNCTION__, netdev->name, addr_ip6);
//DPRINTK("%s, netdev %s IPv6 solicited-node add %pI6\n",__FUNCTION__, netdev->name, addr_ip6_solicited);
break;
}
}
read_unlock_bh(&i6dev->lock);
}
rcu_read_unlock();
if(err) DPRINTK("%s get ipv6 addr failed, use default.\n", __FUNCTION__);
//DPRINTK("%s, netdev %s IPv6 local-link address %pI6\n",__FUNCTION__, netdev->name, addr_ip6);
//DPRINTK("%s, netdev %s IPv6 solicited-node adr %pI6\n",__FUNCTION__, netdev->name, addr_ip6_solicited);
return (err ? NULL : ipval);
}
inline unsigned int fxgmac_tx_avail_desc(struct fxgmac_ring *ring)
{
//return (ring->dma_desc_count - (ring->cur - ring->dirty));
unsigned int avail;
if (ring->dirty > ring->cur)
avail = ring->dirty - ring->cur;
else
avail = ring->dma_desc_count - ring->cur + ring->dirty;
return avail;
}
inline unsigned int fxgmac_rx_dirty_desc(struct fxgmac_ring *ring)
{
//return (ring->cur - ring->dirty);
unsigned int dirty;
if (ring->dirty <= ring->cur)
dirty = ring->cur - ring->dirty;
else
dirty = ring->dma_desc_count - ring->dirty + ring->cur;
return dirty;
}
static int fxgmac_maybe_stop_tx_queue(
struct fxgmac_channel *channel,
struct fxgmac_ring *ring,
unsigned int count)
{
struct fxgmac_pdata *pdata = channel->pdata;
if (count > fxgmac_tx_avail_desc(ring)) {
netif_info(pdata, drv, pdata->netdev,
"Tx queue stopped, not enough descriptors available\n");
netif_stop_subqueue(pdata->netdev, channel->queue_index);
ring->tx.queue_stopped = 1;
/* If we haven't notified the hardware because of xmit_more
* support, tell it now
*/
if (ring->tx.xmit_more)
pdata->hw_ops.tx_start_xmit(channel, ring);
if(netif_msg_tx_done(pdata)) DPRINTK("about stop tx q, ret BUSY\n");
return NETDEV_TX_BUSY;
}
return 0;
}
static void fxgmac_prep_vlan(struct sk_buff *skb,
struct fxgmac_pkt_info *pkt_info)
{
if (skb_vlan_tag_present(skb))
pkt_info->vlan_ctag = skb_vlan_tag_get(skb);
}
static int fxgmac_prep_tso(struct fxgmac_pdata *pdata, struct sk_buff *skb,
struct fxgmac_pkt_info *pkt_info)
{
int ret;
if (!FXGMAC_GET_REG_BITS(pkt_info->attributes,
TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN))
return 0;
ret = skb_cow_head(skb, 0);
if (ret)
return ret;
pkt_info->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
pkt_info->tcp_header_len = tcp_hdrlen(skb);
pkt_info->tcp_payload_len = skb->len - pkt_info->header_len;
pkt_info->mss = skb_shinfo(skb)->gso_size;
if(netif_msg_tx_done(pdata)){
DPRINTK("header_len=%u\n", pkt_info->header_len);
DPRINTK("tcp_header_len=%u, tcp_payload_len=%u\n",
pkt_info->tcp_header_len, pkt_info->tcp_payload_len);
DPRINTK("mss=%u\n", pkt_info->mss);
}
/* Update the number of packets that will ultimately be transmitted
* along with the extra bytes for each extra packet
*/
pkt_info->tx_packets = skb_shinfo(skb)->gso_segs;
pkt_info->tx_bytes += (pkt_info->tx_packets - 1) * pkt_info->header_len;
return 0;
}
static int fxgmac_is_tso(struct sk_buff *skb)
{
if (skb->ip_summed != CHECKSUM_PARTIAL)
return 0;
if (!skb_is_gso(skb))
return 0;
return 1;
}
static void fxgmac_prep_tx_pkt(struct fxgmac_pdata *pdata,
struct fxgmac_ring *ring,
struct sk_buff *skb,
struct fxgmac_pkt_info *pkt_info)
{
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) )
skb_frag_t *frag;
#else
struct skb_frag_struct *frag;
#endif
unsigned int context_desc;
unsigned int len;
unsigned int i;
pkt_info->skb = skb;
context_desc = 0;
pkt_info->desc_count = 0;
pkt_info->tx_packets = 1;
pkt_info->tx_bytes = skb->len;
if(netif_msg_tx_done(pdata))
DPRINTK ("fxgmac_prep_tx_pkt callin,pkt desc cnt=%d,skb len=%d, skbheadlen=%d\n", pkt_info->desc_count, skb->len, skb_headlen(skb));
if (fxgmac_is_tso(skb)) {
/* TSO requires an extra descriptor if mss is different */
if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) {
context_desc = 1;
pkt_info->desc_count++;
}
if(netif_msg_tx_done(pdata))
DPRINTK("fxgmac_is_tso=%d, ip_summed=%d,skb gso=%d\n",((skb->ip_summed == CHECKSUM_PARTIAL) && (skb_is_gso(skb)))?1:0, skb->ip_summed, skb_is_gso(skb)?1:0);
/* TSO requires an extra descriptor for TSO header */
pkt_info->desc_count++;
pkt_info->attributes = FXGMAC_SET_REG_BITS(
pkt_info->attributes,
TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN,
1);
pkt_info->attributes = FXGMAC_SET_REG_BITS(
pkt_info->attributes,
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS,
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN,
1);
if(netif_msg_tx_done(pdata)) DPRINTK ("fxgmac_prep_tx_pkt,tso, pkt desc cnt=%d\n", pkt_info->desc_count);
} else if (skb->ip_summed == CHECKSUM_PARTIAL)
pkt_info->attributes = FXGMAC_SET_REG_BITS(
pkt_info->attributes,
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS,
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN,
1);
if (skb_vlan_tag_present(skb)) {
/* VLAN requires an extra descriptor if tag is different */
if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag)
/* We can share with the TSO context descriptor */
if (!context_desc) {
context_desc = 1;
pkt_info->desc_count++;
}
pkt_info->attributes = FXGMAC_SET_REG_BITS(
pkt_info->attributes,
TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN,
1);
if(netif_msg_tx_done(pdata)) DPRINTK ("fxgmac_prep_tx_pkt,VLAN, pkt desc cnt=%d,vlan=0x%04x\n", pkt_info->desc_count, skb_vlan_tag_get(skb));
}
for (len = skb_headlen(skb); len;) {
pkt_info->desc_count++;
len -= min_t(unsigned int, len, FXGMAC_TX_MAX_BUF_SIZE);
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
for (len = skb_frag_size(frag); len; ) {
pkt_info->desc_count++;
len -= min_t(unsigned int, len, FXGMAC_TX_MAX_BUF_SIZE);
}
}
if(netif_msg_tx_done(pdata))
DPRINTK ("fxgmac_prep_tx_pkt callout,pkt desc cnt=%d,skb len=%d, skbheadlen=%d,frags=%d\n", pkt_info->desc_count, skb->len, skb_headlen(skb), skb_shinfo(skb)->nr_frags);
}
static int fxgmac_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu)
{
unsigned int rx_buf_size;
if (mtu > FXGMAC_JUMBO_PACKET_MTU) {
netdev_alert(netdev, "MTU exceeds maximum supported value\n");
return -EINVAL;
}
rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
rx_buf_size = clamp_val(rx_buf_size, FXGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE * 4 /* follow yonggang's suggestion */);
rx_buf_size = (rx_buf_size + FXGMAC_RX_BUF_ALIGN - 1) &
~(FXGMAC_RX_BUF_ALIGN - 1);
return rx_buf_size;
}
static void fxgmac_enable_rx_tx_ints(struct fxgmac_pdata *pdata)
{
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
struct fxgmac_channel *channel;
enum fxgmac_int int_id;
unsigned int i;
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (channel->tx_ring && channel->rx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_TI_RI;
else if (channel->tx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_TI;
else if (channel->rx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_RI;
else
continue;
hw_ops->enable_int(channel, int_id);
}
}
#if 0
static void fxgmac_disable_rx_tx_ints(struct fxgmac_pdata *pdata)
{
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
struct fxgmac_channel *channel;
enum fxgmac_int int_id;
unsigned int i;
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (channel->tx_ring && channel->rx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_TI_RI;
else if (channel->tx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_TI;
else if (channel->rx_ring)
int_id = FXGMAC_INT_DMA_CH_SR_RI;
else
continue;
hw_ops->disable_int(channel, int_id);
}
}
#endif
static void fxgmac_phy_process(struct fxgmac_pdata *pdata)
{
int cur_link = 0;
int regval = 0;
int cur_speed = 0;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
regval = hw_ops->get_ephy_state(pdata);
// We should make sure that PHY is done with the reset
if (regval & MGMT_EPHY_CTRL_STA_EPHY_RESET) {
pdata->expansion.phy_link = false;
return;
}
cur_link = FXGMAC_GET_REG_BITS(regval,
MGMT_EPHY_CTRL_STA_EPHY_LINKUP_POS,
MGMT_EPHY_CTRL_STA_EPHY_LINKUP_LEN);
if (pdata->expansion.phy_link != cur_link) {
pdata->expansion.phy_link = cur_link;
if (pdata->expansion.phy_link) {
cur_speed = FXGMAC_GET_REG_BITS(regval,
MGMT_EPHY_CTRL_STA_SPEED_POS,
MGMT_EPHY_CTRL_STA_SPEED_LEN);
pdata->phy_speed = (cur_speed == 2) ? SPEED_1000 :
(cur_speed == 1) ? SPEED_100 : SPEED_10;
pdata->phy_duplex = FXGMAC_GET_REG_BITS(regval,
MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_POS,
MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_LEN);
hw_ops->config_mac_speed(pdata);
hw_ops->enable_rx(pdata);
hw_ops->enable_tx(pdata);
netif_carrier_on(pdata->netdev);
if (netif_running(pdata->netdev))
{
netif_tx_wake_all_queues(pdata->netdev);
DPRINTK("%s now is link up, mac_speed=%d.\n", FXGMAC_DRV_NAME,
pdata->phy_speed);
}
}else {
netif_carrier_off(pdata->netdev);
netif_tx_stop_all_queues(pdata->netdev);
pdata->phy_speed = SPEED_UNKNOWN;
pdata->phy_duplex = DUPLEX_UNKNOWN;
hw_ops->disable_rx(pdata);
hw_ops->disable_tx(pdata);
DPRINTK("%s now is link down\n", FXGMAC_DRV_NAME);
}
}
}
static int fxgmac_phy_poll(struct napi_struct *napi, int budget)
{
struct fxgmac_pdata *pdata = container_of(napi,
struct fxgmac_pdata,
expansion.napi_phy);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
fxgmac_phy_process(pdata);
if (napi_complete_done(napi, 0))
hw_ops->enable_msix_one_interrupt(pdata, MSI_ID_PHY_OTHER);
return 0;
}
static irqreturn_t fxgmac_phy_isr(int irq, void *data)
{
struct fxgmac_pdata *pdata = data;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
u32 regval;
regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0);
if (!(regval & MGMT_INT_CTRL0_INT_STATUS_PHY))
return IRQ_HANDLED;
hw_ops->disable_msix_one_interrupt(pdata, MSI_ID_PHY_OTHER);
hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL);
if (napi_schedule_prep(&pdata->expansion.napi_phy)) {
__napi_schedule_irqoff(&pdata->expansion.napi_phy);
}
return IRQ_HANDLED;
}
static irqreturn_t fxgmac_isr(int irq, void *data)
{
unsigned int dma_isr, dma_ch_isr, mac_isr;
struct fxgmac_pdata *pdata = data;
struct fxgmac_channel *channel;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
unsigned int i, ti, ri;
u32 val;
dma_isr = readreg(pdata->pAdapter, pdata->mac_regs + DMA_ISR);
val = readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0);
if (!(val & MGMT_INT_CTRL0_INT_STATUS_RXTXPHY_MASK))
return IRQ_HANDLED;
hw_ops->disable_mgm_interrupt(pdata);
pdata->expansion.mgm_intctrl_val = val;
pdata->stats.mgmt_int_isr++;
for (i = 0; i < pdata->channel_count; i++) {
channel = pdata->channel_head + i;
dma_ch_isr = readl(FXGMAC_DMA_REG(channel, DMA_CH_SR));
netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n",
i, dma_ch_isr);
/* The TI or RI interrupt bits may still be set even if using
* per channel DMA interrupts. Check to be sure those are not
* enabled before using the private data napi structure.
*/
ti = FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TI_POS,
DMA_CH_SR_TI_LEN);
ri = FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RI_POS,
DMA_CH_SR_RI_LEN);
if (!pdata->per_channel_irq && (ti || ri)) {
if (napi_schedule_prep(&pdata->expansion.napi)) {
pdata->stats.napi_poll_isr++;
/* Turn on polling */
__napi_schedule_irqoff(&pdata->expansion.napi);
}
}
if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TPS_POS,
DMA_CH_SR_TPS_LEN))
pdata->stats.tx_process_stopped++;
if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RPS_POS,
DMA_CH_SR_RPS_LEN))
pdata->stats.rx_process_stopped++;
if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TBU_POS,
DMA_CH_SR_TBU_LEN))
pdata->stats.tx_buffer_unavailable++;
if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS,
DMA_CH_SR_RBU_LEN))
pdata->stats.rx_buffer_unavailable++;
/* Restart the device on a Fatal Bus Error */
if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS,
DMA_CH_SR_FBE_LEN)) {
pdata->stats.fatal_bus_error++;
schedule_work(&pdata->expansion.restart_work);
}
/* Clear all interrupt signals */
writel(dma_ch_isr, FXGMAC_DMA_REG(channel, DMA_CH_SR));
}
if (FXGMAC_GET_REG_BITS(dma_isr, DMA_ISR_MACIS_POS,
DMA_ISR_MACIS_LEN)) {
mac_isr = readl(pdata->mac_regs + MAC_ISR);
if (FXGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCTXIS_POS,
MAC_ISR_MMCTXIS_LEN))
hw_ops->tx_mmc_int(pdata);
if (FXGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCRXIS_POS,
MAC_ISR_MMCRXIS_LEN))
hw_ops->rx_mmc_int(pdata);
/* Clear all interrupt signals */
writel(mac_isr, (pdata->mac_regs + MAC_ISR));
}
if (pdata->expansion.mgm_intctrl_val & MGMT_INT_CTRL0_INT_STATUS_PHY) {
hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, &val);
if (napi_schedule_prep(&pdata->expansion.napi)) {
pdata->stats.napi_poll_isr++;
/* Turn on polling */
__napi_schedule_irqoff(&pdata->expansion.napi);
}
}
return IRQ_HANDLED;
}
static irqreturn_t fxgmac_dma_isr(int irq, void *data)
{
struct fxgmac_channel *channel = data;
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
u32 regval;
int message_id;
if (irq == channel->expansion.dma_irq_tx) {
message_id = MSI_ID_TXQ0;
hw_ops->disable_msix_one_interrupt(pdata, message_id);
regval = 0;
regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_SR_TI_POS, DMA_CH_SR_TI_LEN, 1);
writereg(pdata->pAdapter, regval, FXGMAC_DMA_REG(channel, DMA_CH_SR));
if (napi_schedule_prep(&channel->expansion.napi_tx)) {
__napi_schedule_irqoff(&channel->expansion.napi_tx);
}
} else {
message_id = channel->queue_index;
hw_ops->disable_msix_one_interrupt(pdata, message_id);
regval = 0;
regval = readreg(pdata->pAdapter, FXGMAC_DMA_REG(channel, DMA_CH_SR));
regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_SR_RI_POS, DMA_CH_SR_RI_LEN, 1);
writereg(pdata->pAdapter, regval, FXGMAC_DMA_REG(channel, DMA_CH_SR));
if (napi_schedule_prep(&channel->expansion.napi_rx)) {
__napi_schedule_irqoff(&channel->expansion.napi_rx);
}
}
return IRQ_HANDLED;
}
#if 0
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
static void fxgmac_tx_timer(struct timer_list *t)
#else
static void fxgmac_tx_timer(unsigned long data)
#endif
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
struct fxgmac_channel *channel = from_timer(channel, t, tx_timer);
#else
struct fxgmac_channel *channel = (struct fxgmac_channel *)data;
#endif
//for msix. since this function is defined for MSIx, it is no need to brace by macro CONFIG_PCI_MSI
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
u32 regval;
if (pdata->per_channel_irq) {
hw_ops->disable_msix_one_interrupt(pdata, MSI_ID_TXQ0);
regval = 0;
regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_SR_TI_POS, DMA_CH_SR_TI_LEN, 1);
writereg(pdata->pAdapter, regval, FXGMAC_DMA_REG(channel, DMA_CH_SR));
if (napi_schedule_prep(&channel->expansion.napi_tx)) {
pdata->stats.napi_poll_txtimer++;
__napi_schedule(&channel->expansion.napi_tx);
}
} else {
fxgmac_disable_rx_tx_ints(pdata);
if (napi_schedule_prep(&pdata->expansion.napi)) {
pdata->stats.napi_poll_txtimer++;
__napi_schedule(&pdata->expansion.napi);
}
}
pdata->stats.cnt_alive_txtimer++;
channel->tx_timer_active = 0;
}
#endif
#if FXGMAC_TX_HANG_TIMER_EN
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
static void fxgmac_tx_hang_timer_handler(struct timer_list *t)
#else
static void fxgmac_tx_hang_timer_handler(unsigned long data)
#endif
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
struct fxgmac_channel *channel = from_timer(channel, t, expansion.tx_hang_timer);
#else
struct fxgmac_channel *channel = (struct fxgmac_channel *)data;
#endif
#if FXGMAC_TX_HANG_CHECH_DIRTY
struct fxgmac_ring *ring = channel->tx_ring;
#endif
struct fxgmac_pdata *pdata = channel->pdata;
struct net_device *netdev = pdata->netdev;
unsigned int hw_reg_cur;
unsigned int regval;
#if FXGMAC_TX_HANG_CHECH_DIRTY
hw_reg_cur = ring->dirty;
#else
hw_reg_cur = readl(FXGMAC_DMA_REG(channel, 0x44/* tx desc curr pointer reg */));
#endif
if(hw_reg_cur == channel->expansion.tx_hang_hw_cur) {
/* hw current desc still stucked */
if(!pdata->tx_hang_restart_queuing) {
pdata->tx_hang_restart_queuing = 1;
DPRINTK("tx_hang_timer_handler: restart scheduled, at desc %u, queuing=%u.\n", channel->expansion.tx_hang_hw_cur, pdata->tx_hang_restart_queuing);
netif_tx_stop_all_queues(netdev);
/* Disable MAC Rx */
regval = readl(pdata->mac_regs + MAC_CR);
regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS,
MAC_CR_CST_LEN, 0);
regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS,
MAC_CR_ACS_LEN, 0);
regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS,
MAC_CR_RE_LEN, 0);
writel(regval, pdata->mac_regs + MAC_CR);
schedule_work(&pdata->expansion.restart_work);
}
}
channel->expansion.tx_hang_timer_active = 0;
}
static void fxgmac_tx_hang_timer_start(struct fxgmac_channel *channel)
{
struct fxgmac_pdata *pdata = channel->pdata;
/* Start the Tx hang timer */
if (1 && !channel->expansion.tx_hang_timer_active) {
channel->expansion.tx_hang_timer_active = 1;
/* FXGMAC_INIT_DMA_TX_USECS is desc3 polling period, we give 2 more checking period */
mod_timer(&channel->expansion.tx_hang_timer,
jiffies + usecs_to_jiffies(FXGMAC_INIT_DMA_TX_USECS * 10));
}
}
#endif
#if 0
static void fxgmac_init_timers(struct fxgmac_pdata *pdata)
{
struct fxgmac_channel *channel;
unsigned int i;
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (!channel->tx_ring)
break;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
timer_setup(&channel->tx_timer, fxgmac_tx_timer, 0);
#else
setup_timer(&channel->tx_timer, fxgmac_tx_timer, (unsigned long)channel);
#endif
#if FXGMAC_TX_HANG_TIMER_EN
channel->tx_hang_timer_active = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
timer_setup(&channel->tx_hang_timer, fxgmac_tx_hang_timer_handler, 0);
#else
setup_timer(&channel->tx_hang_timer, fxgmac_tx_hang_timer_handler, (unsigned long)channel);
#endif
#endif
}
}
static void fxgmac_stop_timers(struct fxgmac_pdata *pdata)
{
struct fxgmac_channel *channel;
unsigned int i;
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (!channel->tx_ring)
break;
del_timer_sync(&channel->tx_timer);
#if FXGMAC_TX_HANG_TIMER_EN
del_timer_sync(&channel->tx_hang_timer);
channel->tx_hang_timer_active = 0;
#endif
}
}
}
#endif
static void fxgmac_napi_enable(struct fxgmac_pdata *pdata, unsigned int add)
{
struct fxgmac_channel *channel;
unsigned int i;
if (pdata->per_channel_irq) {
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (add) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0))
netif_napi_add_weight(pdata->netdev, &channel->expansion.napi_rx,
fxgmac_one_poll_rx, NAPI_POLL_WEIGHT);
#else
netif_napi_add(pdata->netdev, &channel->expansion.napi_rx,
fxgmac_one_poll_rx, NAPI_POLL_WEIGHT);
#endif
}
napi_enable(&channel->expansion.napi_rx);
if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0))
netif_napi_add_weight(pdata->netdev, &channel->expansion.napi_tx,
fxgmac_one_poll_tx, NAPI_POLL_WEIGHT);
#else
netif_napi_add(pdata->netdev, &channel->expansion.napi_tx,
fxgmac_one_poll_tx, NAPI_POLL_WEIGHT);
#endif
napi_enable(&channel->expansion.napi_tx);
}
if(netif_msg_drv(pdata)) DPRINTK("napi_enable, msix ch%d napi enabled done,add=%d\n", i, add);
}
// for phy
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0))
netif_napi_add_weight(pdata->netdev, &pdata->expansion.napi_phy,
fxgmac_phy_poll, NAPI_POLL_WEIGHT);
#else
netif_napi_add(pdata->netdev, &pdata->expansion.napi_phy,
fxgmac_phy_poll, NAPI_POLL_WEIGHT);
#endif
napi_enable(&pdata->expansion.napi_phy);
} else {
i = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_NAPI_FREE_POS,
FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN);
if (!i) {
if (add) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0))
netif_napi_add_weight(pdata->netdev, &pdata->expansion.napi,
fxgmac_all_poll, NAPI_POLL_WEIGHT);
#else
netif_napi_add(pdata->netdev, &pdata->expansion.napi,
fxgmac_all_poll, NAPI_POLL_WEIGHT);
#endif
}
napi_enable(&pdata->expansion.napi);
pdata->expansion.int_flags = FXGMAC_SET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_NAPI_FREE_POS,
FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN,
1);
}
}
}
static void fxgmac_napi_disable(struct fxgmac_pdata *pdata, unsigned int del)
{
struct fxgmac_channel *channel;
unsigned int i;
if (pdata->per_channel_irq) {
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
napi_disable(&channel->expansion.napi_rx);
if (del) {
netif_napi_del(&channel->expansion.napi_rx);
}
if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
napi_disable(&channel->expansion.napi_tx);
netif_napi_del(&channel->expansion.napi_tx);
}
if(netif_msg_drv(pdata)) DPRINTK("napi_disable, msix ch%d napi disabled done,del=%d\n", i, del);
}
napi_disable(&pdata->expansion.napi_phy);
netif_napi_del(&pdata->expansion.napi_phy);
}
} else {
i = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_NAPI_FREE_POS,
FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN);
if (i) {
napi_disable(&pdata->expansion.napi);
if (del)
netif_napi_del(&pdata->expansion.napi);
pdata->expansion.int_flags = FXGMAC_SET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_NAPI_FREE_POS,
FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN,
0);
}
}
}
static int fxgmac_request_irqs(struct fxgmac_pdata *pdata)
{
struct net_device *netdev = pdata->netdev;
struct fxgmac_channel *channel;
unsigned int i;
int ret;
u32 msi, msix, need_free;
msi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_MSI_POS,
FXGMAC_FLAG_MSI_LEN);
msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_MSIX_POS,
FXGMAC_FLAG_MSIX_LEN);
need_free = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_IRQ_FREE_POS,
FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN);
if(!msix) {
if (!need_free) {
ret = devm_request_irq(pdata->dev, pdata->dev_irq, fxgmac_isr,
msi ? 0 : IRQF_SHARED,
netdev->name, pdata);
if (ret) {
netdev_alert(netdev, "error requesting irq %d, ret = %d\n", pdata->dev_irq, ret);
return ret;
}
pdata->expansion.int_flags = FXGMAC_SET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_IRQ_FREE_POS,
FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN,
1);
}
}
if (!pdata->per_channel_irq)
return 0;
ret = devm_request_irq(pdata->dev, pdata->expansion.phy_irq, fxgmac_phy_isr, 0, netdev->name, pdata);
if (ret) {
netdev_alert(netdev, "error requesting phy irq %d, ret = %d\n", pdata->expansion.phy_irq, ret);
return ret;
}
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++) {
snprintf(channel->expansion.dma_irq_name,
sizeof(channel->expansion.dma_irq_name) - 1,
"%s-ch%d-Rx-%u", netdev_name(netdev), i,
channel->queue_index);
if(FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
snprintf(channel->expansion.dma_irq_name_tx,
sizeof(channel->expansion.dma_irq_name_tx) - 1,
"%s-ch%d-Tx-%u", netdev_name(netdev), i,
channel->queue_index);
ret = devm_request_irq(pdata->dev, channel->expansion.dma_irq_tx,
fxgmac_dma_isr, 0,
channel->expansion.dma_irq_name_tx, channel);
if (ret) {
DPRINTK("fxgmac_req_irqs, err with MSIx irq request for ch %d tx, ret=%d\n", i, ret);
/* Using an unsigned int, 'i' will go to UINT_MAX and exit */
devm_free_irq(pdata->dev, channel->expansion.dma_irq_tx, channel);
//devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
return ret;
}
if(netif_msg_drv(pdata))
DPRINTK("fxgmac_req_irqs, MSIx irq_tx request ok, ch=%d, irq=%d,%s\n",
i, channel->expansion.dma_irq_tx, channel->expansion.dma_irq_name_tx);
}
ret = devm_request_irq(pdata->dev, channel->dma_irq,
fxgmac_dma_isr, 0,
channel->expansion.dma_irq_name, channel);
if (ret) {
netdev_alert(netdev, "error requesting irq %d\n",
channel->dma_irq);
goto err_irq;
}
}
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_req_irqs, MSIx irq request ok, total=%d,%d~%d\n", i, (pdata->channel_head)[0].dma_irq, (pdata->channel_head)[i-1].dma_irq);
return 0;
err_irq:
DPRINTK("fxgmac_req_irqs, err with MSIx irq request at %d, ret=%d\n", i, ret);
if (pdata->per_channel_irq) {
for (i--, channel--; i < pdata->channel_count; i--, channel--) {
if(FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
devm_free_irq(pdata->dev, channel->expansion.dma_irq_tx, channel);
}
devm_free_irq(pdata->dev, channel->dma_irq, channel);
}
devm_free_irq(pdata->dev, pdata->expansion.phy_irq, pdata);
}
return ret;
}
static void fxgmac_free_irqs(struct fxgmac_pdata *pdata)
{
struct fxgmac_channel *channel;
unsigned int i = 0;
u32 need_free, msix;
msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_MSIX_POS,
FXGMAC_FLAG_MSIX_LEN);
need_free = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_IRQ_FREE_POS,
FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN);
if(!msix) {
if (need_free) {
devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
pdata->expansion.int_flags = FXGMAC_SET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_IRQ_FREE_POS,
FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN,
0);
}
}
if (!pdata->per_channel_irq)
return;
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
if(FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
devm_free_irq(pdata->dev, channel->expansion.dma_irq_tx, channel);
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_free_irqs, MSIx irq_tx clear done, ch=%d\n", i);
}
devm_free_irq(pdata->dev, channel->dma_irq, channel);
}
devm_free_irq(pdata->dev, pdata->expansion.phy_irq, pdata);
}
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_free_irqs, MSIx rx irq clear done, total=%d\n", i);
}
void fxgmac_free_tx_data(struct fxgmac_pdata *pdata)
{
struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops;
struct fxgmac_desc_data *desc_data;
struct fxgmac_channel *channel;
struct fxgmac_ring *ring;
unsigned int i, j;
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
ring = channel->tx_ring;
if (!ring)
break;
for (j = 0; j < ring->dma_desc_count; j++) {
desc_data = FXGMAC_GET_DESC_DATA(ring, j);
desc_ops->unmap_desc_data(pdata, desc_data);
}
}
}
}
void fxgmac_free_rx_data(struct fxgmac_pdata *pdata)
{
struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops;
struct fxgmac_desc_data *desc_data;
struct fxgmac_channel *channel;
struct fxgmac_ring *ring;
unsigned int i, j;
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
ring = channel->rx_ring;
if (!ring)
break;
for (j = 0; j < ring->dma_desc_count; j++) {
desc_data = FXGMAC_GET_DESC_DATA(ring, j);
desc_ops->unmap_desc_data(pdata, desc_data);
}
}
}
}
/*
* since kernel does not clear the MSI mask bits and
* this function clear MSI mask bits when MSI is enabled.
*/
static int fxgmac_disable_pci_msi_config(struct pci_dev *pdev)
{
u16 pcie_cap_offset;
u32 pcie_msi_mask_bits;
int ret = 0;
pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_MSI);
if (pcie_cap_offset) {
ret = pci_read_config_dword(pdev, pcie_cap_offset, &pcie_msi_mask_bits);
if (ret) {
printk(KERN_ERR "read pci config space MSI cap. failed, %d\n", ret);
ret = -EFAULT;
}
}
pcie_msi_mask_bits = FXGMAC_SET_REG_BITS(pcie_msi_mask_bits,
PCI_CAP_ID_MSI_ENABLE_POS,
PCI_CAP_ID_MSI_ENABLE_LEN,
0);
ret = pci_write_config_dword(pdev, pcie_cap_offset, pcie_msi_mask_bits);
if (ret) {
printk(KERN_ERR "write pci config space MSI mask failed, %d\n", ret);
ret = -EFAULT;
}
return ret;
}
static int fxgmac_disable_pci_msix_config(struct pci_dev *pdev)
{
u16 pcie_cap_offset;
u32 pcie_msi_mask_bits;
int ret = 0;
pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
if (pcie_cap_offset) {
ret = pci_read_config_dword(pdev, pcie_cap_offset, &pcie_msi_mask_bits);
if (ret) {
printk(KERN_ERR "read pci config space MSIX cap. failed, %d\n", ret);
ret = -EFAULT;
}
}
pcie_msi_mask_bits = FXGMAC_SET_REG_BITS(pcie_msi_mask_bits,
PCI_CAP_ID_MSIX_ENABLE_POS,
PCI_CAP_ID_MSIX_ENABLE_LEN,
0);
ret = pci_write_config_dword(pdev, pcie_cap_offset, pcie_msi_mask_bits);
if (ret) {
printk(KERN_ERR "write pci config space MSIX mask failed, %d\n", ret);
ret = -EFAULT;
}
return ret;
}
extern int fxgmac_dismiss_all_int(struct fxgmac_pdata *pdata);
int fxgmac_start(struct fxgmac_pdata *pdata)
{
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
struct net_device *netdev = pdata->netdev;
int ret;
unsigned int pcie_low_power = 0;
u32 regval;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac start callin here.\n");
/* must reset software again here, to avoid flushing tx queue error caused by the system only run probe
* when installing driver on the arm platform.
*/
hw_ops->exit(pdata);
if (FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_LEGACY_POS,
FXGMAC_FLAG_LEGACY_LEN)) {
/*
* we should disable msi and msix here when we use legacy interrupt,for two reasons:
* 1. Exit will restore msi and msix config regisiter, that may enable them.
* 2. When the driver that uses the msix interrupt by default is compiled
* into the OS, uninstall the driver through rmmod, and then install the
* driver that uses the legacy interrupt, at which time the msix enable
* will be turned on again by default after waking up from S4 on some platform.
* such as UOS platform.
*/
ret = fxgmac_disable_pci_msi_config(pdata->pdev);
ret |= fxgmac_disable_pci_msix_config(pdata->pdev);
if (ret)
return ret;
}
hw_ops->reset_phy(pdata);
hw_ops->release_phy(pdata);
hw_ops->pcie_init(pdata,
pcie_low_power & PCIE_LP_ASPM_LTR,
pcie_low_power & PCIE_LP_ASPM_L1SS,
pcie_low_power & PCIE_LP_ASPM_L1,
pcie_low_power & PCIE_LP_ASPM_L0S);
hw_ops->config_power_up(pdata);
fxgmac_dismiss_all_int(pdata);
ret = hw_ops->init(pdata);
if (ret) {
printk("fxgmac hw init error.\n");
return ret;
}
fxgmac_napi_enable(pdata, 1);
ret = fxgmac_request_irqs(pdata);
if (ret)
goto err_napi;
hw_ops->enable_tx(pdata);
hw_ops->enable_rx(pdata);
//config interrupt to level signal
regval = (u32)readl((const volatile void *)(pdata->mac_regs + DMA_MR));
regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_INTM_POS,
DMA_MR_INTM_LEN, 1);
regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_QUREAD_POS,
DMA_MR_QUREAD_LEN, 1);
writel(regval, pdata->mac_regs + DMA_MR);
writel(0xF0000000, (volatile void *)(netdev->base_addr + MGMT_INT_CTRL0));
hw_ops->set_interrupt_moderation(pdata);
if (pdata->per_channel_irq)
hw_ops->enable_msix_rxtxphyinterrupt(pdata);
fxgmac_enable_rx_tx_ints(pdata);
#if 0
netif_tx_start_all_queues(netdev);
#endif
hw_ops->led_under_active(pdata);
return 0;
err_napi:
fxgmac_napi_disable(pdata, 1);
hw_ops->exit(pdata);
DPRINTK("fxgmac start callout with irq err.\n");
return ret;
}
void fxgmac_stop(struct fxgmac_pdata *pdata)
{
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
struct net_device *netdev = pdata->netdev;
struct fxgmac_channel *channel;
struct netdev_queue *txq;
unsigned int i;
if (pdata->per_channel_irq) {
hw_ops->disable_msix_interrupt(pdata);
}
else {
hw_ops->disable_mgm_interrupt(pdata);
}
pdata->expansion.phy_link = false;
netif_carrier_off(netdev);//yzhang added, 0324
netif_tx_stop_all_queues(netdev);
#if 0
fxgmac_stop_timers(pdata);
#endif
hw_ops->disable_tx(pdata);
hw_ops->disable_rx(pdata);
fxgmac_free_irqs(pdata);
fxgmac_napi_disable(pdata, 1);
channel = pdata->channel_head;
if (channel != NULL) {
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (!channel->tx_ring)
continue;
txq = netdev_get_tx_queue(netdev, channel->queue_index);
netdev_tx_reset_queue(txq);
}
}
switch (pdata->expansion.current_state) {
case CURRENT_STATE_SUSPEND:
hw_ops->led_under_sleep(pdata);
break;
case CURRENT_STATE_SHUTDOWN:
case CURRENT_STATE_RESTART:
hw_ops->led_under_shutdown(pdata);
break;
case CURRENT_STATE_CLOSE:
//hw_ops->led_under_disable(pdata);
break;
default:
break;
}
}
void fxgmac_restart_dev(struct fxgmac_pdata *pdata)
{
int ret;
/* If not running, "restart" will happen on open */
if (!netif_running(pdata->netdev))
return;
pdata->expansion.current_state = CURRENT_STATE_RESTART;
fxgmac_stop(pdata);
fxgmac_free_tx_data(pdata);
fxgmac_free_rx_data(pdata);
ret = fxgmac_start(pdata);
if (ret) {
printk("fxgmac_restart_dev: fxgmac_start failed.\n");
}
}
static void fxgmac_restart(struct work_struct *work)
{
struct fxgmac_pdata *pdata = container_of(work,
struct fxgmac_pdata,
expansion.restart_work);
rtnl_lock();
fxgmac_restart_dev(pdata);
rtnl_unlock();
}
void fxgmac_net_powerup(struct fxgmac_pdata *pdata)
{
int ret;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_net_powerup callin\n");
/* signal that we are up now */
pdata->expansion.powerstate = 0; //clear all bits as normal now
if (__test_and_set_bit(FXGMAC_POWER_STATE_UP, &pdata->expansion.powerstate)) {
return; /* do nothing if already up */
}
ret = fxgmac_start(pdata);
if (ret) {
printk("fxgmac_net_powerup: fxgmac_start error\n");
return;
}
// must call it after fxgmac_start,because it will be enable in fxgmac_start
hw_ops->disable_arp_offload(pdata);
if(netif_msg_drv(pdata)) {
DPRINTK("fxgmac_net_powerup callout, powerstate=%ld.\n", pdata->expansion.powerstate);
}
}
void fxgmac_net_powerdown(struct fxgmac_pdata *pdata, unsigned int wol)
{
struct net_device *netdev = pdata->netdev;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_net_powerdown callin here.\n");
/* signal that we are down to the interrupt handler */
if (__test_and_set_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate))
return; /* do nothing if already down */
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_net_powerdown continue with down process.\n");
/* phy polling timer should detect the state of fxgmac and stop link status polling accordingly */
__clear_bit(FXGMAC_POWER_STATE_UP, &pdata->expansion.powerstate);
#if 1
/* Shut off incoming Tx traffic */
netif_tx_stop_all_queues(netdev);
/* call carrier off first to avoid false dev_watchdog timeouts */
netif_carrier_off(netdev);
netif_tx_disable(netdev);
/* Disable Rx */
hw_ops->disable_rx(pdata);
/* synchronize_rcu() needed for pending XDP buffers to drain */
//if (adapter->xdp_ring[0]) 20210709
synchronize_rcu();
fxgmac_stop(pdata); //some works are redundent in this call
#endif
// must call it after software reset
hw_ops->pre_power_down(pdata, false);
/* set mac to lowpower mode and enable wol accordingly */
hw_ops->config_power_down(pdata, wol);
#if 1
//handle vfs if it is envolved
//similar work as in restart() for that, we do need a resume laterly
fxgmac_free_tx_data(pdata);
fxgmac_free_rx_data(pdata);
#endif
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_net_powerdown callout, powerstate=%ld.\n", pdata->expansion.powerstate);
}
static int fxgmac_open(struct net_device *netdev)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_desc_ops *desc_ops;
int ret;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_open callin\n");
desc_ops = &pdata->desc_ops;
/* TODO: Initialize the phy */
/* Calculate the Rx buffer size before allocating rings */
//DPRINTK("fxgmac_open, b4 calc rx buf size, mtu,min,max=%d,%d,%d.\n", netdev->mtu, netdev->min_mtu, netdev->max_mtu);
ret = fxgmac_calc_rx_buf_size(netdev, netdev->mtu);
if (ret < 0)
return ret;
pdata->rx_buf_size = ret;
/* Allocate the channels and rings */
ret = desc_ops->alloc_channles_and_rings(pdata);
if (ret)
return ret;
INIT_WORK(&pdata->expansion.restart_work, fxgmac_restart);
#if 0
fxgmac_init_timers(pdata);
#endif
ret = fxgmac_start(pdata);
if (ret)
goto err_channels_and_rings;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_open callout\n");
return 0;
err_channels_and_rings:
desc_ops->free_channels_and_rings(pdata);
DPRINTK("fxgmac_open callout with channel alloc err\n");
return ret;
}
static int fxgmac_close(struct net_device *netdev)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_desc_ops *desc_ops;
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_close callin\n");
desc_ops = &pdata->desc_ops;
pdata->expansion.current_state = (pdata->expansion.current_state == CURRENT_STATE_SHUTDOWN) ?
pdata->expansion.current_state : CURRENT_STATE_CLOSE;
/* Stop the device */
fxgmac_stop(pdata);
/* Free the channels and rings */
desc_ops->free_channels_and_rings(pdata);
pdata->hw_ops.reset_phy(pdata);
if(netif_msg_drv(pdata)) DPRINTK("fxgmac_close callout\n");
return 0;
}
#if ((LINUX_VERSION_CODE > KERNEL_VERSION(4,0,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)))
static void fxgmac_tx_timeout(struct net_device *netdev)
#else
static void fxgmac_tx_timeout(struct net_device *netdev, unsigned int unused)
#endif
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
netdev_warn(netdev, "tx timeout, device restarting\n");
#if FXGMAC_TX_HANG_TIMER_EN
if(!pdata->tx_hang_restart_queuing)
schedule_work(&pdata->expansion.restart_work);
#else
schedule_work(&pdata->expansion.restart_work);
#endif
}
static int fxgmac_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_pkt_info *tx_pkt_info;
struct fxgmac_desc_ops *desc_ops;
struct fxgmac_channel *channel;
struct fxgmac_hw_ops *hw_ops;
struct netdev_queue *txq;
struct fxgmac_ring *ring;
int ret;
desc_ops = &pdata->desc_ops;
hw_ops = &pdata->hw_ops;
//yzhang disabled
if(netif_msg_tx_done(pdata)) DPRINTK("xmit callin, skb->len=%d,q=%d\n", skb->len, skb->queue_mapping);
channel = pdata->channel_head + skb->queue_mapping;
txq = netdev_get_tx_queue(netdev, channel->queue_index);
ring = channel->tx_ring;
tx_pkt_info = &ring->pkt_info;
#if 0
if (1/*yzhang dbg skb->len == 0*/) {
netif_err(pdata, tx_err, netdev,
"empty skb received from stack\n");
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
#else
if (skb->len == 0) {
netif_err(pdata, tx_err, netdev,
"empty skb received from stack\n");
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
#endif
/* Prepare preliminary packet info for TX */
memset(tx_pkt_info, 0, sizeof(*tx_pkt_info));
fxgmac_prep_tx_pkt(pdata, ring, skb, tx_pkt_info);
/* Check that there are enough descriptors available */
ret = fxgmac_maybe_stop_tx_queue(channel, ring,
tx_pkt_info->desc_count);
if (ret)
{
return ret;
}
ret = fxgmac_prep_tso(pdata, skb, tx_pkt_info);
if (ret) {
netif_err(pdata, tx_err, netdev,
"error processing TSO packet\n");
DPRINTK("dev_xmit,tx err for TSO\n");
dev_kfree_skb_any(skb);
return ret;
}
fxgmac_prep_vlan(skb, tx_pkt_info);
if (!desc_ops->map_tx_skb(channel, skb)) {
dev_kfree_skb_any(skb);
DPRINTK("xmit, map tx skb err\n");
return NETDEV_TX_OK;
}
/* Report on the actual number of bytes (to be) sent */
netdev_tx_sent_queue(txq, tx_pkt_info->tx_bytes);
if(netif_msg_tx_done(pdata)) DPRINTK("xmit,before hw_xmit, byte len=%d\n", tx_pkt_info->tx_bytes);
/* Configure required descriptor fields for transmission */
hw_ops->dev_xmit(channel);
#if FXGMAC_DUMMY_TX_DEBUG
DPRINTK("tx hw_ops->dev_xmit ok\n");
#endif
if (netif_msg_pktdata(pdata))
fxgmac_dbg_pkt(netdev, skb, true);
/* Stop the queue in advance if there may not be enough descriptors */
fxgmac_maybe_stop_tx_queue(channel, ring, FXGMAC_TX_MAX_DESC_NR);
return NETDEV_TX_OK;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0))
static void fxgmac_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *s)
#else
static struct rtnl_link_stats64 * fxgmac_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *s)
#endif
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_stats *pstats = &pdata->stats;
#if FXGMAC_PM_FEATURE_ENABLED
/* 20210709 for net power down */
if(!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate))
#endif
{
//DPRINTK("get_stats64, ndo op, callin\n");
pdata->hw_ops.read_mmc_stats(pdata);
}
s->rx_packets = pstats->rxframecount_gb;
s->rx_bytes = pstats->rxoctetcount_gb;
s->rx_errors = pstats->rxframecount_gb -
pstats->rxbroadcastframes_g -
pstats->rxmulticastframes_g -
pstats->rxunicastframes_g;
s->multicast = pstats->rxmulticastframes_g;
s->rx_length_errors = pstats->rxlengtherror;
s->rx_crc_errors = pstats->rxcrcerror;
s->rx_fifo_errors = pstats->rxfifooverflow;
s->tx_packets = pstats->txframecount_gb;
s->tx_bytes = pstats->txoctetcount_gb;
s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
s->tx_dropped = netdev->stats.tx_dropped;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0))
return s;
#endif
}
static int fxgmac_set_mac_address(struct net_device *netdev, void *addr)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
struct sockaddr *saddr = addr;
if (!is_valid_ether_addr(saddr->sa_data))
return -EADDRNOTAVAIL;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0))
eth_hw_addr_set(netdev, saddr->sa_data);
#else
memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len);
#endif
memcpy(pdata->mac_addr, saddr->sa_data, netdev->addr_len);
hw_ops->set_mac_address(pdata, saddr->sa_data);
hw_ops->set_mac_hash(pdata);
DPRINTK("fxgmac,set mac addr to %02x:%02x:%02x:%02x:%02x:%02x\n",netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
return 0;
}
// cmd = [0x89F0, 0x89FF]
static int fxgmac_ioctl(struct net_device *netdev,
struct ifreq *ifr, int cmd)
{
struct file f;
int ret = FXGMAC_SUCCESS;
struct fxgmac_pdata *pdata = netdev_priv(netdev);
if (!netif_running(netdev))
return -ENODEV;
f.private_data = pdata;
switch (cmd) {
case FXGMAC_DEV_CMD:
ret = fxgmac_dbg_netdev_ops_ioctl(&f, FXGMAC_IOCTL_DFS_COMMAND, (unsigned long)(ifr->ifr_data));
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) )
static int fxgmac_siocdevprivate(struct net_device *dev,
struct ifreq *ifr,
void __user *data,
int cmd)
{
return fxgmac_ioctl(dev, ifr, cmd);
}
#endif
static int fxgmac_change_mtu(struct net_device *netdev, int mtu)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
int ret;
#ifdef FXGMAC_DEBUG
int old_mtu = netdev->mtu;
#endif
fxgmac_stop(pdata);
fxgmac_free_tx_data(pdata);
// We must unmap rx desc's dma before we change rx_buf_size.
// Becaues the size of the unmapped DMA is set according to rx_buf_size
fxgmac_free_rx_data(pdata);
pdata->jumbo = mtu > ETH_DATA_LEN ? 1 : 0;
ret = fxgmac_calc_rx_buf_size(netdev, mtu);
if (ret < 0)
return ret;
pdata->rx_buf_size = ret;
netdev->mtu = mtu;
if (netif_running(netdev))
fxgmac_start(pdata);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
DPRINTK("fxgmac,set MTU from %d to %d. min, max=(%d,%d)\n",old_mtu, netdev->mtu, netdev->min_mtu, netdev->max_mtu);
#else
DPRINTK("fxgmac,set MTU from %d to %d.\n",old_mtu, netdev->mtu);
#endif
return 0;
}
static int fxgmac_vlan_rx_add_vid(struct net_device *netdev,
__be16 proto,
u16 vid)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
set_bit(vid, pdata->active_vlans);
#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED
pdata->vlan = vid;
hw_ops->enable_rx_vlan_filtering(pdata);
#else
hw_ops->update_vlan_hash_table(pdata);
#endif
DPRINTK("fxgmac,add rx vlan %d\n", vid);
return 0;
}
static int fxgmac_vlan_rx_kill_vid(struct net_device *netdev,
__be16 proto,
u16 vid)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
clear_bit(vid, pdata->active_vlans);
#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED
pdata->vlan = 0;
hw_ops->disable_rx_vlan_filtering(pdata);
#else
hw_ops->update_vlan_hash_table(pdata);
#endif
DPRINTK("fxgmac,del rx vlan %d\n", vid);
return 0;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void fxgmac_poll_controller(struct net_device *netdev)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_channel *channel;
unsigned int i;
if (pdata->per_channel_irq) {
channel = pdata->channel_head;
for (i = 0; i < pdata->channel_count; i++, channel++)
fxgmac_dma_isr(channel->dma_irq, channel);
} else {
disable_irq(pdata->dev_irq);
fxgmac_isr(pdata->dev_irq, pdata);
enable_irq(pdata->dev_irq);
}
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
static int fxgmac_set_features(struct net_device *netdev,
netdev_features_t features)
{
netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter, tso;
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
int ret = 0;
rxhash = pdata->expansion.netdev_features & NETIF_F_RXHASH;
rxcsum = pdata->expansion.netdev_features & NETIF_F_RXCSUM;
rxvlan = pdata->expansion.netdev_features & NETIF_F_HW_VLAN_CTAG_RX;
rxvlan_filter = pdata->expansion.netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER;
tso = pdata->expansion.netdev_features & (NETIF_F_TSO | NETIF_F_TSO6);
if ((features & (NETIF_F_TSO | NETIF_F_TSO6)) && !tso) {
printk("enable tso.\n");
pdata->hw_feat.tso = 1;
hw_ops->config_tso(pdata);
} else if (!(features & (NETIF_F_TSO | NETIF_F_TSO6)) && tso) {
printk("disable tso.\n");
pdata->hw_feat.tso = 0;
hw_ops->config_tso(pdata);
}
if ((features & NETIF_F_RXHASH) && !rxhash)
ret = hw_ops->enable_rss(pdata);
else if (!(features & NETIF_F_RXHASH) && rxhash)
ret = hw_ops->disable_rss(pdata);
if (ret)
return ret;
if ((features & NETIF_F_RXCSUM) && !rxcsum)
hw_ops->enable_rx_csum(pdata);
else if (!(features & NETIF_F_RXCSUM) && rxcsum)
hw_ops->disable_rx_csum(pdata);
if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan)
hw_ops->enable_rx_vlan_stripping(pdata);
else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan)
hw_ops->disable_rx_vlan_stripping(pdata);
if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter)
hw_ops->enable_rx_vlan_filtering(pdata);
else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter)
hw_ops->disable_rx_vlan_filtering(pdata);
pdata->expansion.netdev_features = features;
DPRINTK("fxgmac,set features done,%llx\n", (u64)features);
return 0;
}
static void fxgmac_set_rx_mode(struct net_device *netdev)
{
struct fxgmac_pdata *pdata = netdev_priv(netdev);
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
hw_ops->config_rx_mode(pdata);
}
static const struct net_device_ops fxgmac_netdev_ops = {
.ndo_open = fxgmac_open,
.ndo_stop = fxgmac_close,
.ndo_start_xmit = fxgmac_xmit,
.ndo_tx_timeout = fxgmac_tx_timeout,
.ndo_get_stats64 = fxgmac_get_stats64,
.ndo_change_mtu = fxgmac_change_mtu,
.ndo_set_mac_address = fxgmac_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = fxgmac_ioctl,
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) )
.ndo_siocdevprivate = fxgmac_siocdevprivate,
#endif
.ndo_vlan_rx_add_vid = fxgmac_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = fxgmac_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = fxgmac_poll_controller,
#endif
.ndo_set_features = fxgmac_set_features,
.ndo_set_rx_mode = fxgmac_set_rx_mode,
};
const struct net_device_ops *fxgmac_get_netdev_ops(void)
{
return &fxgmac_netdev_ops;
}
static void fxgmac_rx_refresh(struct fxgmac_channel *channel)
{
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_ring *ring = channel->rx_ring;
struct fxgmac_desc_data *desc_data;
#if 0
struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops;
#endif
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
while (ring->dirty != ring->cur) {
desc_data = FXGMAC_GET_DESC_DATA(ring, ring->dirty);
#if 0
/* Reset desc_data values */
desc_ops->unmap_desc_data(pdata, desc_data);
if (desc_ops->map_rx_buffer(pdata, ring, desc_data))
break;
#endif
hw_ops->rx_desc_reset(pdata, desc_data, ring->dirty);
ring->dirty = FXGMAC_GET_ENTRY(ring->dirty, ring->dma_desc_count);
}
/* Make sure everything is written before the register write */
wmb();
/* Update the Rx Tail Pointer Register with address of
* the last cleaned entry
*/
desc_data = FXGMAC_GET_DESC_DATA(ring, (ring->dirty - 1) & (ring->dma_desc_count - 1));
writel(lower_32_bits(desc_data->dma_desc_addr), FXGMAC_DMA_REG(channel, DMA_CH_RDTR_LO));
}
static struct sk_buff *fxgmac_create_skb(struct fxgmac_pdata *pdata,
struct napi_struct *napi,
struct fxgmac_desc_data *desc_data,
unsigned int len)
{
#if 0
unsigned int copy_len;
struct sk_buff *skb;
u8 *packet;
skb = napi_alloc_skb(napi, desc_data->rx.hdr.dma_len);
if (!skb)
return NULL;
/* Start with the header buffer which may contain just the header
* or the header plus data
*/
dma_sync_single_range_for_cpu(pdata->dev, desc_data->rx.hdr.dma_base,
desc_data->rx.hdr.dma_off,
desc_data->rx.hdr.dma_len,
DMA_FROM_DEVICE);
packet = page_address(desc_data->rx.hdr.pa.pages) +
desc_data->rx.hdr.pa.pages_offset;
copy_len = (desc_data->rx.hdr_len) ? desc_data->rx.hdr_len : len;
copy_len = min(desc_data->rx.hdr.dma_len, copy_len);
skb_copy_to_linear_data(skb, packet, copy_len);
skb_put(skb, copy_len);
len -= copy_len;
if (len) {
/* Add the remaining data as a frag */
dma_sync_single_range_for_cpu(pdata->dev,
desc_data->rx.buf.dma_base,
desc_data->rx.buf.dma_off,
desc_data->rx.buf.dma_len,
DMA_FROM_DEVICE);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
desc_data->rx.buf.pa.pages,
desc_data->rx.buf.pa.pages_offset,
len, desc_data->rx.buf.dma_len);
desc_data->rx.buf.pa.pages = NULL;
}
return skb;
#endif
struct sk_buff *skb;
skb = __netdev_alloc_skb_ip_align(pdata->netdev, len, GFP_ATOMIC);
if (!skb) {
netdev_err(pdata->netdev, "%s: Rx init fails; skb is NULL\n", __func__);
return NULL;
}
dma_sync_single_for_cpu(pdata->dev, desc_data->rx.buf.dma_base, len, DMA_FROM_DEVICE);
skb_copy_to_linear_data(skb, desc_data->skb->data, len);
skb_put(skb, len);
dma_sync_single_for_device(pdata->dev, desc_data->rx.buf.dma_base, len, DMA_FROM_DEVICE);
return skb;
}
static int fxgmac_tx_poll(struct fxgmac_channel *channel)
{
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_ring *ring = channel->tx_ring;
struct net_device *netdev = pdata->netdev;
unsigned int tx_packets = 0, tx_bytes = 0;
struct fxgmac_desc_data *desc_data;
struct fxgmac_dma_desc *dma_desc;
struct fxgmac_desc_ops *desc_ops;
struct fxgmac_hw_ops *hw_ops;
struct netdev_queue *txq;
int processed = 0;
unsigned int cur;
static int fxgmac_restart_need = 0;
static u32 change_cnt = 0;
static u32 reg_cur_pre = 0xffffffff;
#if FXGMAC_TX_HANG_TIMER_EN
static u32 reg_cur = 0;
#endif
desc_ops = &pdata->desc_ops;
hw_ops = &pdata->hw_ops;
/* Nothing to do if there isn't a Tx ring for this channel */
if (!ring){
if(netif_msg_tx_done(pdata) && (channel->queue_index < pdata->tx_q_count)) DPRINTK("tx_poll, null point to ring %d\n", channel->queue_index);
return 0;
}
if((ring->cur != ring->dirty) && (netif_msg_tx_done(pdata)))
DPRINTK("tx_poll callin, ring_cur=%d,ring_dirty=%d,qIdx=%d\n", ring->cur, ring->dirty, channel->queue_index);
cur = ring->cur;
/* Be sure we get ring->cur before accessing descriptor data */
smp_rmb();
txq = netdev_get_tx_queue(netdev, channel->queue_index);
while (ring->dirty != cur) {
desc_data = FXGMAC_GET_DESC_DATA(ring, ring->dirty);
dma_desc = desc_data->dma_desc;
if (!hw_ops->tx_complete(dma_desc)) {
#if FXGMAC_TRIGGER_TX_HANG
struct net_device *netdev = pdata->netdev;
#define FXGMAC_HANG_THRESHOLD 1
//static u32 reg_tail = 0, reg_tail_pre = 0xffffffff;
reg_cur = readl(FXGMAC_DMA_REG(channel, 0x44/* tx desc curr pointer reg */));
if(reg_cur != reg_cur_pre){
reg_cur_pre = reg_cur;
change_cnt = 0;
} else {
change_cnt++;
}
if (change_cnt > 2)
{
//change_cnt = 0;
DPRINTK("after complete check, cur=%d, dirty=%d,qIdx=%d, hw desc cur=%#x, pre=%#x\n", ring->cur, ring->dirty, channel->queue_index,
reg_cur, reg_cur_pre);
if((ring->cur > ring->dirty) && ((ring->cur - ring->dirty) > FXGMAC_HANG_THRESHOLD) ) {
DPRINTK("after complete check warning..., too many TBD occupied by HW, 0xdbbb, %d.\n", (ring->cur - ring->dirty));
(* ((u32 *)(netdev->base_addr + 0x1000))) = 0xdbbb;
if(!fxgmac_restart_need ) {
schedule_work(&pdata->expansion.restart_work);
fxgmac_restart_need = 1;
change_cnt = 0;
}
}else if((ring->cur < ring->dirty) && ((ring->cur + (ring->dma_desc_count - ring->dirty)) > FXGMAC_HANG_THRESHOLD) ) {
DPRINTK("after complete check warning..., too many TBD occupied by HW, 0xdb00, %d.\n", (ring->cur + (ring->dma_desc_count - ring->dirty)));
(* ((u32 *)(netdev->base_addr + 0x1000))) = 0xdb00;
if(!fxgmac_restart_need ) {
schedule_work(&pdata->expansion.restart_work);
fxgmac_restart_need = 1;
change_cnt = 0;
}
}
}
#endif
#if FXGMAC_TX_HANG_TIMER_EN
if((!pdata->tx_hang_restart_queuing) && (!channel->expansion.tx_hang_timer_active)) {
reg_cur = ring->dirty;
if(reg_cur_pre != reg_cur) {
reg_cur_pre = reg_cur;
change_cnt = 0;
}else {
change_cnt++;
}
if (change_cnt > 4)
{
#if FXGMAC_TX_HANG_CHECH_DIRTY
channel->expansion.tx_hang_hw_cur = ring->dirty;
#else
channel->expansion.tx_hang_hw_cur = readl(FXGMAC_DMA_REG(channel, 0x44/* tx desc curr pointer reg */));
#endif
/* double check for race conditione */
if ((!pdata->tx_hang_restart_queuing) && (!channel->expansion.tx_hang_timer_active)) {
DPRINTK("tx_hang polling: start timer at desc %u, timer act=%u, queuing=%u, qidx=%u.\n", reg_cur, channel->expansion.tx_hang_timer_active, pdata->tx_hang_restart_queuing, channel->queue_index);
fxgmac_tx_hang_timer_start(channel);
}
}
}else if (pdata->tx_hang_restart_queuing) {
//if(netif_msg_drv(pdata)) DPRINTK("tx_hang_timer_handler: restart scheduled.\n");
}
#endif
break;
}
reg_cur_pre = 0xffffffff;
fxgmac_restart_need = 0;
change_cnt = 0;
/* Make sure descriptor fields are read after reading
* the OWN bit
*/
dma_rmb();
if (netif_msg_tx_done(pdata))
fxgmac_dump_tx_desc(pdata, ring, ring->dirty, 1, 0);
if (hw_ops->is_last_desc(dma_desc)) {
tx_packets += desc_data->tx.packets;
tx_bytes += desc_data->tx.bytes;
}
/* Free the SKB and reset the descriptor for re-use */
desc_ops->unmap_desc_data(pdata, desc_data);
hw_ops->tx_desc_reset(desc_data);
processed++;
//ring->dirty++;
ring->dirty = FXGMAC_GET_ENTRY(ring->dirty, ring->dma_desc_count);
}
if (!processed)
return 0;
netdev_tx_completed_queue(txq, tx_packets, tx_bytes);
if ((ring->tx.queue_stopped == 1) &&
(fxgmac_tx_avail_desc(ring) > FXGMAC_TX_DESC_MIN_FREE)) {
ring->tx.queue_stopped = 0;
netif_tx_wake_queue(txq);
}
//yzhang comment out to reduce print
if(netif_msg_tx_done(pdata)){
DPRINTK("tx_poll callout, processed=%d\n", processed);
}
return processed;
}
extern void fxgmac_print_pkt(struct net_device *netdev,
struct sk_buff *skb, bool tx_rx);
static int fxgmac_rx_poll(struct fxgmac_channel *channel, int budget)
{
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_ring *ring = channel->rx_ring;
struct net_device *netdev = pdata->netdev;
unsigned int len;
unsigned int context_next, context;
struct fxgmac_desc_data *desc_data;
struct fxgmac_pkt_info *pkt_info;
unsigned int incomplete;
struct fxgmac_hw_ops *hw_ops;
struct napi_struct *napi;
struct sk_buff *skb;
int packet_count = 0;
u32 ipce,iphe;
hw_ops = &pdata->hw_ops;
/* Nothing to do if there isn't a Rx ring for this channel */
if (!ring)
return 0;
incomplete = 0;
context_next = 0;
napi = (pdata->per_channel_irq) ? &channel->expansion.napi_rx : &pdata->expansion.napi;
desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur);
pkt_info = &ring->pkt_info;
while (packet_count < budget) {
memset(pkt_info, 0, sizeof(*pkt_info));
skb = NULL;
len = 0;
read_again:
desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur);
if (fxgmac_rx_dirty_desc(ring) > FXGMAC_RX_DESC_MAX_DIRTY)
fxgmac_rx_refresh(channel);
if (hw_ops->dev_read(channel))
break;
ring->cur = FXGMAC_GET_ENTRY(ring->cur, ring->dma_desc_count);
incomplete = FXGMAC_GET_REG_BITS(
pkt_info->attributes,
RX_PACKET_ATTRIBUTES_INCOMPLETE_POS,
RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN);
context_next = FXGMAC_GET_REG_BITS(
pkt_info->attributes,
RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS,
RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN);
context = FXGMAC_GET_REG_BITS(
pkt_info->attributes,
RX_PACKET_ATTRIBUTES_CONTEXT_POS,
RX_PACKET_ATTRIBUTES_CONTEXT_LEN);
if (incomplete || context_next)
goto read_again;
if (pkt_info->errors) {
netif_err(pdata, rx_err, netdev, "error in received packet\n");
dev_kfree_skb(skb);
goto next_packet;
}
if (!context) {
len = desc_data->rx.len;
if (len > pdata->rx_buf_size) {
if (net_ratelimit())
netdev_err(pdata->netdev,"len %d larger than size (%d)\n", len, pdata->rx_buf_size);
pdata->netdev->stats.rx_dropped++;
goto next_packet;
}
if (len == 0) {
if (net_ratelimit())
netdev_err(pdata->netdev,"A packet of length 0 was received\n");
pdata->netdev->stats.rx_length_errors++;
goto next_packet;
}
if (len && !skb) {
skb = fxgmac_create_skb(pdata, napi, desc_data, len);
if (unlikely(!skb)) {
if (net_ratelimit())
netdev_warn(pdata->netdev, "create skb failed\n");
goto next_packet;
}
}
}
if (!skb)
goto next_packet;
if(netif_msg_pktdata(pdata))
fxgmac_print_pkt(netdev, skb, false);
skb_checksum_none_assert(skb);
if (netdev->features & NETIF_F_RXCSUM)
{
ipce = FXGMAC_GET_REG_BITS_LE(desc_data->dma_desc->desc1,
RX_NORMAL_DESC1_WB_IPCE_POS,
RX_NORMAL_DESC1_WB_IPCE_LEN);
iphe = FXGMAC_GET_REG_BITS_LE(desc_data->dma_desc->desc1,
RX_NORMAL_DESC1_WB_IPHE_POS,
RX_NORMAL_DESC1_WB_IPHE_LEN);
/* if csum error,let the stack verify checksum errors.otherwise don't verify */
if (!ipce && !iphe && FXGMAC_GET_REG_BITS(pkt_info->attributes,
RX_PACKET_ATTRIBUTES_CSUM_DONE_POS,
RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN))
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
if (FXGMAC_GET_REG_BITS(pkt_info->attributes,
RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) {
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
pkt_info->vlan_ctag);
pdata->stats.rx_vlan_packets++;
}
if (FXGMAC_GET_REG_BITS(pkt_info->attributes,
RX_PACKET_ATTRIBUTES_RSS_HASH_POS,
RX_PACKET_ATTRIBUTES_RSS_HASH_LEN))
skb_set_hash(skb, pkt_info->rss_hash,
pkt_info->rss_hash_type);
skb->dev = netdev;
skb->protocol = eth_type_trans(skb, netdev);
skb_record_rx_queue(skb, channel->queue_index);
if(pdata->expansion.fxgmac_test_tso_flag)
{
/* tso test */
if(pdata->expansion.fxgmac_test_tso_seg_num == 1)
{
/* last segment */
if(pdata->expansion.fxgmac_test_last_tso_len == skb->len + FXGMAC_TEST_MAC_HEAD_LEN)
{
/* receive last segment, reset flag */
pdata->expansion.fxgmac_test_tso_flag = false;
pdata->expansion.fxgmac_test_tso_seg_num = 0;
pdata->expansion.fxgmac_test_packet_len = 0;
pdata->expansion.fxgmac_test_last_tso_len = 0;
/* process packet */
if((pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT != pdata->expansion.fxgmac_test_skb_arr_out_index){
struct sk_buff *tmpskb = skb_copy(skb, GFP_ATOMIC);
skb_push(tmpskb, FXGMAC_TEST_MAC_HEAD_LEN);
pdata->expansion.fxgmac_test_skb_array[pdata->expansion.fxgmac_test_skb_arr_in_index] = tmpskb;
pdata->expansion.fxgmac_test_skb_arr_in_index = (pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT;
}
else{
DPRINTK("loopback test buffer is full.");
}
}
}
else /* non last segment */
{
if(pdata->expansion.fxgmac_test_packet_len == skb->len + FXGMAC_TEST_MAC_HEAD_LEN){
/* receive a segment */
pdata->expansion.fxgmac_test_tso_seg_num--;
/* process packet */
if((pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT != pdata->expansion.fxgmac_test_skb_arr_out_index){
struct sk_buff *tmpskb = skb_copy(skb, GFP_ATOMIC);
skb_push(tmpskb, FXGMAC_TEST_MAC_HEAD_LEN);
pdata->expansion.fxgmac_test_skb_array[pdata->expansion.fxgmac_test_skb_arr_in_index] = tmpskb;
pdata->expansion.fxgmac_test_skb_arr_in_index = (pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT;
}
else{
DPRINTK("loopback test buffer is full.");
}
}
}
}
else if(pdata->expansion.fxgmac_test_packet_len != 0)
{
/* xsum and phy loopback test */
if(pdata->expansion.fxgmac_test_packet_len == skb->len + FXGMAC_TEST_MAC_HEAD_LEN)
{
/* reset fxg_packet_len */
pdata->expansion.fxgmac_test_packet_len = 0;
if((pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT != pdata->expansion.fxgmac_test_skb_arr_out_index){
struct sk_buff *tmpskb = skb_copy(skb, GFP_ATOMIC);
skb_push(tmpskb, FXGMAC_TEST_MAC_HEAD_LEN);
pdata->expansion.fxgmac_test_skb_array[pdata->expansion.fxgmac_test_skb_arr_in_index] = tmpskb;
pdata->expansion.fxgmac_test_skb_arr_in_index = (pdata->expansion.fxgmac_test_skb_arr_in_index + 1) % FXGMAC_MAX_DBG_TEST_PKT;
}
else{
DPRINTK("loopback test buffer is full.");
}
}
}
#if 1
napi_gro_receive(napi, skb);
#else
netif_receive_skb(skb);
#endif
next_packet:
packet_count++;
pdata->netdev->stats.rx_packets++;
pdata->netdev->stats.rx_bytes += len;
}
fxgmac_rx_refresh(channel);
return packet_count;
}
static int fxgmac_one_poll_tx(struct napi_struct *napi, int budget)
{
struct fxgmac_channel *channel = container_of(napi,
struct fxgmac_channel,
expansion.napi_tx);
int ret = 0;
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
ret = fxgmac_tx_poll(channel);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
if (napi_complete_done(napi, 0)) {
hw_ops->enable_msix_one_interrupt(pdata, MSI_ID_TXQ0);
}
#else
napi_complete(napi);
hw_ops->enable_msix_one_interrupt(pdata, MSI_ID_TXQ0);
#endif
return 0;
}
static int fxgmac_one_poll_rx(struct napi_struct *napi, int budget)
{
struct fxgmac_channel *channel = container_of(napi,
struct fxgmac_channel,
expansion.napi_rx);
int processed = 0;
struct fxgmac_pdata *pdata = channel->pdata;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
processed = fxgmac_rx_poll(channel, budget);
if (processed < budget) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
/* if there no interrupt occured when this interrupt running,struct napi's state is NAPIF_STATE_SCHED,
* napi_complete_done return true and we can enable irq,it will not cause unbalanced iqr issure.
* if there more interrupt occured when this interrupt running,struct napi's state is NAPIF_STATE_SCHED | NAPIF_STATE_MISSED
* because napi_schedule_prep will make it. At this time napi_complete_done will return false and
* schedule poll again because of NAPIF_STATE_MISSED,it will cause unbalanced irq issure.
*/
if (napi_complete_done(napi, processed)) {
hw_ops->enable_msix_one_interrupt(pdata, channel->queue_index);
}
#else
napi_complete(napi);
hw_ops->enable_msix_one_interrupt(pdata, channel->queue_index);
#endif
}
return processed;
}
static int fxgmac_all_poll(struct napi_struct *napi, int budget)
{
struct fxgmac_pdata *pdata = container_of(napi,
struct fxgmac_pdata,
expansion.napi);
struct fxgmac_channel *channel;
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
int processed;
unsigned int i;
//yzhang comment out
if(netif_msg_rx_status(pdata)){
DPRINTK("rx all_poll callin budget=%d\n", budget);
}
processed = 0;
do {
channel = pdata->channel_head;
/* Cleanup Tx ring first */
/*since only 1 tx channel supported in this version, poll ch 0 always. */
fxgmac_tx_poll(pdata->channel_head + 0);
for (i = 0; i < pdata->channel_count; i++, channel++) {
processed += fxgmac_rx_poll(channel, budget);
}
} while (false);
// for phy, we needn't to process any packet,so processed will be 0
if (pdata->expansion.mgm_intctrl_val & MGMT_INT_CTRL0_INT_STATUS_PHY) {
fxgmac_phy_process(pdata);
pdata->expansion.mgm_intctrl_val &= ~MGMT_INT_CTRL0_INT_STATUS_PHY;
}
/* If we processed everything, we are done */
if (processed < budget) {
/* Turn off polling */
if (napi_complete_done(napi, processed))
hw_ops->enable_mgm_interrupt(pdata);
}
if((processed) && (netif_msg_rx_status(pdata))) { //yzhang for debug
DPRINTK("rx all_poll callout received = %d\n", processed);
}
return processed;
}