📄 ag7100.c
字号:
if (!handled) { assert(0); printk(MODULE_NAME ": unhandled intr isr %#x\n", isr); } return IRQ_HANDLED;} /* * Rx and Tx DMA hangs and goes to an invalid state in HOWL boards * when the link partner is forced to 10/100 Mode.By resetting the MAC * we are able to recover from this state.This is a software WAR and * will be removed once we have a hardware fix. */#ifdef CONFIG_AR9100void ag7100_dma_reset(ag7100_mac_t *mac){ uint32_t mask; if(mac->mac_unit) mask = AR7100_RESET_GE1_MAC; else mask = AR7100_RESET_GE0_MAC; ar7100_reg_rmw_set(AR7100_RESET, mask); mdelay(100); ar7100_reg_rmw_clear(AR7100_RESET, mask); mdelay(100); ag7100_intr_disable_recv(mac); schedule_work(&mac->mac_tx_timeout);}#endifstatic intag7100_poll(struct net_device *dev, int *budget){ ag7100_mac_t *mac = (ag7100_mac_t *)dev->priv; int work_done, max_work = min(*budget, dev->quota), status = 0; ag7100_rx_status_t ret; u32 flags; ret = ag7100_recv_packets(dev, mac, max_work, &work_done); dev->quota -= work_done; *budget -= work_done;#ifdef CONFIG_AR9100 if(ret == AG7100_RX_DMA_HANG) { status = 0; netif_rx_complete(dev); ag7100_dma_reset(mac); return status; }#endif if (likely(ret == AG7100_RX_STATUS_DONE)) { netif_rx_complete(dev); spin_lock_irqsave(&mac->mac_lock, flags); ag7100_intr_enable_recv(mac); spin_unlock_irqrestore(&mac->mac_lock, flags); } else if (likely(ret == AG7100_RX_STATUS_NOT_DONE)) { /* * We have work left */ status = 1; } else if (ret == AG7100_RX_STATUS_OOM) { printk(MODULE_NAME ": oom..?\n"); /* * Start timer, stop polling, but do not enable rx interrupts. */ mod_timer(&mac->mac_oom_timer, jiffies+1); netif_rx_complete(dev); } return status;}static intag7100_recv_packets(struct net_device *dev, ag7100_mac_t *mac, int quota, int *work_done){ ag7100_ring_t *r = &mac->mac_rxring; ag7100_desc_t *ds; ag7100_buffer_t *bp; struct sk_buff *skb; ag7100_rx_status_t ret = AG7100_RX_STATUS_DONE; int head = r->ring_head, len, status, iquota = quota, more_pkts, rep; ag7100_trc(iquota,"iquota");#if !defined(CONFIG_AR9100) status = ag7100_reg_rd(mac, AG7100_DMA_RX_STATUS);#endifprocess_pkts: ag7100_trc(status,"status");#if !defined(CONFIG_AR9100) /* * Under stress, the following assertion fails. * * On investigation, the following `appears' to happen. * - pkts received * - rx intr * - poll invoked * - process received pkts * - replenish buffers * - pkts received * * - NO RX INTR & STATUS REG NOT UPDATED <--- * * - s/w doesn't process pkts since no intr * - eventually, no more buffers for h/w to put * future rx pkts * - RX overflow intr * - poll invoked * - since status reg is not getting updated * following assertion fails.. * * Ignore the status register. Regardless of this * being a rx or rx overflow, we have packets to process. * So, we go ahead and receive the packets.. */ assert((status & AG7100_RX_STATUS_PKT_RCVD)); assert((status >> 16));#endif /* * Flush the DDR FIFOs for our gmac */ ar7100_flush_ge(mac->mac_unit); assert(quota > 0); /* WCL */ while(quota) { ds = &r->ring_desc[head]; ag7100_trc(head,"hd"); ag7100_trc(ds, "ds"); if (ag7100_rx_owned_by_dma(ds)) {#ifdef CONFIG_AR9100 if(quota == iquota) { *work_done = quota = 0; return AG7100_RX_DMA_HANG; }#else assert(quota != iquota); /* WCL */#endif break; } ag7100_intr_ack_rx(mac); bp = &r->ring_buffer[head]; len = ds->pkt_size; skb = bp->buf_pkt; assert(skb); skb_put(skb, len - ETHERNET_FCS_SIZE);#if defined(CONFIG_ATHRS26_PHY) && defined(HEADER_EN) uint8_t type; uint16_t def_vid; if(mac->mac_unit == 0) { type = (skb->data[1]) & 0xf; if (type == NORMAL_PACKET) {#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) /*cpu egress tagged*/ if (is_cpu_egress_tagged()) { if ((skb->data[12 + HEADER_LEN] != 0x81) || (skb->data[13 + HEADER_LEN] != 0x00)) { def_vid = athrs26_defvid_get(skb->data[0] & 0xf); skb_push(skb, 2); /* vid lenghth - header length */ memmove(&skb->data[0], &skb->data[4], 12); /*remove header and add vlan tag*/ skb->data[12] = 0x81; skb->data[13] = 0x00; skb->data[14] = (def_vid >> 8) & 0xf; skb->data[15] = def_vid & 0xff; } } else#endif skb_pull(skb, 2); /* remove attansic header */ mac->net_rx_packets ++; mac->net_rx_bytes += skb->len; /* * also pulls the ether header */ skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; bp->buf_pkt = NULL; dev->last_rx = jiffies; quota--; netif_receive_skb(skb); } else { mac->net_rx_packets ++; mac->net_rx_bytes += skb->len; bp->buf_pkt = NULL; dev->last_rx = jiffies; quota--; if (type == READ_WRITE_REG_ACK) { header_receive_skb(skb); } else { kfree_skb(skb); } } }else { mac->net_rx_packets ++; mac->net_rx_bytes += skb->len; /* * also pulls the ether header */ skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; bp->buf_pkt = NULL; dev->last_rx = jiffies; quota--; netif_receive_skb(skb); }#else mac->net_rx_packets ++; mac->net_rx_bytes += skb->len; /* * also pulls the ether header */ skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; bp->buf_pkt = NULL; dev->last_rx = jiffies; quota--; netif_receive_skb(skb);#endif ag7100_ring_incr(head); } assert(iquota != quota); r->ring_head = head; rep = ag7100_rx_replenish(mac);#ifdef CONFIG_AR9100 if(rep < 0) { *work_done =0 ; return AG7100_RX_DMA_HANG; }#endif /* * let's see what changed while we were slogging. * ack Rx in the loop above is no flush version. It will get flushed now. */ status = ag7100_reg_rd(mac, AG7100_DMA_RX_STATUS); more_pkts = (status & AG7100_RX_STATUS_PKT_RCVD); ag7100_trc(more_pkts,"more_pkts"); if (!more_pkts) goto done; /* * more pkts arrived; if we have quota left, get rolling again */ if (quota) goto process_pkts; /* * out of quota */ ret = AG7100_RX_STATUS_NOT_DONE;done: *work_done = (iquota - quota); if (unlikely(ag7100_rx_ring_full(mac))) return AG7100_RX_STATUS_OOM; /* * !oom; if stopped, restart h/w */ if (unlikely(status & AG7100_RX_STATUS_OVF)) { mac->net_rx_over_errors ++; ag7100_intr_ack_rxovf(mac); ag7100_rx_start(mac); } return ret;}static struct sk_buff * ag7100_buffer_alloc(void){ struct sk_buff *skb; skb = dev_alloc_skb(AG7100_RX_BUF_SIZE); if (unlikely(!skb)) return NULL; skb_reserve(skb, AG7100_RX_RESERVE); return skb;}static voidag7100_buffer_free(struct sk_buff *skb){ if (in_irq()) dev_kfree_skb_irq(skb); else dev_kfree_skb(skb);}/* * Head is the first slot with a valid buffer. Tail is the last slot * replenished. Tries to refill buffers from tail to head. */static intag7100_rx_replenish(ag7100_mac_t *mac){ ag7100_ring_t *r = &mac->mac_rxring; int head = r->ring_head, tail = r->ring_tail, refilled = 0; ag7100_desc_t *ds; ag7100_buffer_t *bf; ag7100_trc(head,"hd"); ag7100_trc(tail,"tl"); do { bf = &r->ring_buffer[tail]; ds = &r->ring_desc[tail]; ag7100_trc(ds,"ds");#ifdef CONFIG_AR9100 if(ag7100_rx_owned_by_dma(ds)) { return -1; }#else assert(!ag7100_rx_owned_by_dma(ds));#endif assert(!bf->buf_pkt); bf->buf_pkt = ag7100_buffer_alloc(); if (!bf->buf_pkt) { printk(MODULE_NAME ": outta skbs!\n"); break; } dma_cache_inv((unsigned long)bf->buf_pkt->data, AG7100_RX_BUF_SIZE); ds->pkt_start_addr = virt_to_phys(bf->buf_pkt->data); ag7100_rx_give_to_dma(ds); refilled ++; ag7100_ring_incr(tail); } while(tail != head); /* * Flush descriptors */ wmb(); r->ring_tail = tail; ag7100_trc(refilled,"refilled"); return refilled;}/* * Reap from tail till the head or whenever we encounter an unxmited packet. */static intag7100_tx_reap(ag7100_mac_t *mac){ ag7100_ring_t *r = &mac->mac_txring; int head = r->ring_head, tail = r->ring_tail, reaped = 0, i; ag7100_desc_t *ds; ag7100_buffer_t *bf; uint32_t flags; ag7100_trc_new(head,"hd"); ag7100_trc_new(tail,"tl"); ar7100_flush_ge(mac->mac_unit); while(tail != head) { ds = &r->ring_desc[tail]; ag7100_trc_new(ds,"ds"); if(ag7100_tx_owned_by_dma(ds)) break; bf = &r->ring_buffer[tail]; assert(bf->buf_pkt); ag7100_trc_new(bf->buf_lastds,"lastds"); if(ag7100_tx_owned_by_dma(bf->buf_lastds)) break; for(i = 0; i < bf->buf_nds; i++) { ag7100_intr_ack_tx(mac); ag7100_ring_incr(tail); } ag7100_buffer_free(bf->buf_pkt); bf->buf_pkt = NULL; reaped ++; } r->ring_tail = tail; if (netif_queue_stopped(mac->mac_dev) && (ag7100_ndesc_unused(mac, r) >= AG7100_TX_QSTART_THRESH) && netif_carrier_ok(mac->mac_dev)) { if (ag7100_reg_rd(mac, AG7100_DMA_INTR_MASK) & AG7100_INTR_TX) { spin_lock_irqsave(&mac->mac_lock, flags); ag7100_intr_disable_tx(mac);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -