📄 ag7100.c
字号:
ag7100_set_pll(mac, SW_PLL); }#else ag7100_set_pll(mac, 0x00991099);#endif ag7100_reg_rmw_clear(mac, AG7100_MAC_FIFO_CFG_5, (1 << 19)); break; default: assert(0); }#ifdef CONFIG_ATHRS26_PHY if(change_flag) athrs26_phy_on(mac);#endif printk(MODULE_NAME ": cfg_1: %#x\n", ag7100_reg_rd(mac, AG7100_MAC_FIFO_CFG_1)); printk(MODULE_NAME ": cfg_2: %#x\n", ag7100_reg_rd(mac, AG7100_MAC_FIFO_CFG_2)); printk(MODULE_NAME ": cfg_3: %#x\n", ag7100_reg_rd(mac, AG7100_MAC_FIFO_CFG_3)); printk(MODULE_NAME ": cfg_4: %#x\n", ag7100_reg_rd(mac, AG7100_MAC_FIFO_CFG_4)); printk(MODULE_NAME ": cfg_5: %#x\n", ag7100_reg_rd(mac, AG7100_MAC_FIFO_CFG_5));}/* * phy link state management */static intag7100_check_link(ag7100_mac_t *mac){ struct net_device *dev = mac->mac_dev; int carrier = netif_carrier_ok(dev), fdx, phy_up; ag7100_phy_speed_t speed; int rc; /* The vitesse switch uses an indirect method to communicate phy status * so it is best to limit the number of calls to what is necessary. * However a single call returns all three pieces of status information. * * This is a trivial change to the other PHYs ergo this change. * */ rc = ag7100_get_link_status(mac->mac_unit, &phy_up, &fdx, &speed); if (rc < 0) goto done; if (!phy_up) { if (carrier) { printk(MODULE_NAME ": unit %d: phy not up carrier %d\n", mac->mac_unit, carrier); netif_carrier_off(dev); netif_stop_queue(dev); } goto done; } /* * phy is up. Either nothing changed or phy setttings changed while we * were sleeping. */ if ((fdx < 0) || (speed < 0)) { printk(MODULE_NAME ": phy not connected?\n"); return 0; } if (carrier && (speed == mac->mac_speed) && (fdx == mac->mac_fdx)) goto done; printk(MODULE_NAME ": unit %d phy is up...", mac->mac_unit); printk("%s %s %s\n", mii_str[mac->mac_unit][mii_if(mac)], spd_str[speed], dup_str[fdx]); ag7100_set_mac_from_link(mac, speed, fdx); printk(MODULE_NAME ": done cfg2 %#x ifctl %#x miictrl %#x \n", ag7100_reg_rd(mac, AG7100_MAC_CFG2), ag7100_reg_rd(mac, AG7100_MAC_IFCTL), ar7100_reg_rd(mii_reg(mac))); /* * in business */ netif_carrier_on(dev); netif_start_queue(dev);done: mod_timer(&mac->mac_phy_timer, jiffies + AG7100_PHY_POLL_SECONDS*HZ); return 0;}static voidag7100_choose_phy(uint32_t phy_addr){#ifdef CONFIG_AR7100_EMULATION if (phy_addr == 0x10) { ar7100_reg_rmw_set(AR7100_MII0_CTRL, (1 << 6)); } else { ar7100_reg_rmw_clear(AR7100_MII0_CTRL, (1 << 6)); }#endif}uint16_tag7100_mii_read(int unit, uint32_t phy_addr, uint8_t reg){ ag7100_mac_t *mac = ag7100_unit2mac(0); uint16_t addr = (phy_addr << AG7100_ADDR_SHIFT) | reg, val; volatile int rddata; uint16_t ii = 0x1000; ag7100_choose_phy(phy_addr); ag7100_reg_wr(mac, AG7100_MII_MGMT_CMD, 0x0); ag7100_reg_wr(mac, AG7100_MII_MGMT_ADDRESS, addr); ag7100_reg_wr(mac, AG7100_MII_MGMT_CMD, AG7100_MGMT_CMD_READ); do { udelay(5); rddata = ag7100_reg_rd(mac, AG7100_MII_MGMT_IND) & 0x1; }while(rddata && --ii); val = ag7100_reg_rd(mac, AG7100_MII_MGMT_STATUS); ag7100_reg_wr(mac, AG7100_MII_MGMT_CMD, 0x0); return val;}voidag7100_mii_write(int unit, uint32_t phy_addr, uint8_t reg, uint16_t data){ ag7100_mac_t *mac = ag7100_unit2mac(0); uint16_t addr = (phy_addr << AG7100_ADDR_SHIFT) | reg; volatile int rddata; uint16_t ii = 0x1000; ag7100_choose_phy(phy_addr); ag7100_reg_wr(mac, AG7100_MII_MGMT_ADDRESS, addr); ag7100_reg_wr(mac, AG7100_MII_MGMT_CTRL, data); do { rddata = ag7100_reg_rd(mac, AG7100_MII_MGMT_IND) & 0x1; }while(rddata && --ii);}/* * Tx operation: * We do lazy reaping - only when the ring is "thresh" full. If the ring is * full and the hardware is not even done with the first pkt we q'd, we turn * on the tx interrupt, stop all q's and wait for h/w to * tell us when its done with a "few" pkts, and then turn the Qs on again. * * Locking: * The interrupt only touches the ring when Q's stopped => Tx is lockless, * except when handling ring full. * * Desc Flushing: Flushing needs to be handled at various levels, broadly: * - The DDr FIFOs for desc reads. * - WB's for desc writes. */static voidag7100_handle_tx_full(ag7100_mac_t *mac){ u32 flags; assert(!netif_queue_stopped(mac->mac_dev)); mac->mac_net_stats.tx_fifo_errors ++; netif_stop_queue(mac->mac_dev); spin_lock_irqsave(&mac->mac_lock, flags); ag7100_intr_enable_tx(mac); spin_unlock_irqrestore(&mac->mac_lock, flags);}/* ****************************** * * Code under test - do not use * * ****************************** */static ag7100_desc_t *ag7100_get_tx_ds(ag7100_mac_t *mac, int *len, unsigned char **start){ ag7100_desc_t *ds; int len_this_ds; ag7100_ring_t *r = &mac->mac_txring; /* force extra pkt if remainder less than 4 bytes */ if (*len > tx_len_per_ds) if (*len < (tx_len_per_ds + 4)) len_this_ds = tx_len_per_ds - 4; else len_this_ds = tx_len_per_ds; else len_this_ds = *len; ds = &r->ring_desc[r->ring_head]; ag7100_trc_new(ds,"ds addr"); ag7100_trc_new(ds,"ds len");#ifdef CONFIG_AR9100 if(ag7100_tx_owned_by_dma(ds)) ag7100_dma_reset(mac);#else assert(!ag7100_tx_owned_by_dma(ds));#endif ds->pkt_size = len_this_ds; ds->pkt_start_addr = virt_to_phys(*start); ds->more = 1; *len -= len_this_ds; *start += len_this_ds; ag7100_ring_incr(r->ring_head); return ds;}#if defined(CONFIG_ATHRS26_PHY)int#elsestatic int#endifag7100_hard_start(struct sk_buff *skb, struct net_device *dev){ ag7100_mac_t *mac = (ag7100_mac_t *)dev->priv; ag7100_ring_t *r = &mac->mac_txring; ag7100_buffer_t *bp; ag7100_desc_t *ds, *fds; unsigned char *start; int len; int nds_this_pkt;#if defined(CONFIG_AR9100) && defined(CONFIG_VITESSE_8601_7395_PHY) static int vsc73xx_config;#endif#if defined(CONFIG_AR9100) && defined(CONFIG_VITESSE_8601_7395_PHY) if (unlikely(vsc73xx_config == 0)) { vsc73xx_wr(7,0,5,0x33); vsc73xx_config = 1; }#endif#ifdef VSC73XX_DEBUG { static int vsc73xx_dbg; if (vsc73xx_dbg == 0) { vsc73xx_get_link_status_dbg(); vsc73xx_dbg = 1; } vsc73xx_dbg = (vsc73xx_dbg + 1) % 10; }#endif#if defined(CONFIG_ATHRS26_PHY) && defined(HEADER_EN) /* add header to normal frames */ /* check if normal frames */ if ((mac->mac_unit == 0) && (!((skb->cb[0] == 0x7f) && (skb->cb[1] == 0x5d)))) { skb_push(skb, HEADER_LEN); skb->data[0] = 0x10; /* broadcast = 0; from_cpu = 0; reserved = 1; port_num = 0 */ skb->data[1] = 0x80; /* reserved = 0b10; priority = 0; type = 0 (normal) */ }#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_HLEN + HEADER_LEN + 4)))) { /*vlan tag length = 4*/ printk(MODULE_NAME ": [%d] bad skb, dev->mtu=%d,ETH_HLEN=%d len %d\n", mac->mac_unit, dev->mtu, ETH_HLEN, skb->len); goto dropit; }#else if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_HLEN + HEADER_LEN)))) { printk(MODULE_NAME ": [%d] bad skb, dev->mtu=%d,ETH_HLEN=%d len %d\n", mac->mac_unit, dev->mtu, ETH_HLEN, skb->len); goto dropit; }#endif #else#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_HLEN + 4)))) { /*vlan tag length = 4*/ printk(MODULE_NAME ": bad skb, len %d\n", skb->len); goto dropit; }#else if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_HLEN)))) { printk(MODULE_NAME ": bad skb, len %d\n", skb->len); goto dropit; }#endif #endif if (ag7100_tx_reap_thresh(mac)) ag7100_tx_reap(mac); ag7100_trc_new(r->ring_head,"hard-stop hd"); ag7100_trc_new(r->ring_tail,"hard-stop tl"); ag7100_trc_new(skb->len, "len this pkt"); ag7100_trc_new(skb->data, "ptr 2 pkt"); dma_cache_wback((unsigned long)skb->data, skb->len); bp = &r->ring_buffer[r->ring_head]; bp->buf_pkt = skb; len = skb->len; start = skb->data; assert(len>4); nds_this_pkt = 1; fds = ds = ag7100_get_tx_ds(mac, &len, &start); while (len>0) { ds = ag7100_get_tx_ds(mac, &len, &start); nds_this_pkt++; ag7100_tx_give_to_dma(ds); } ds->more = 0; ag7100_tx_give_to_dma(fds); bp->buf_lastds = ds; bp->buf_nds = nds_this_pkt; ag7100_trc_new(ds,"last ds"); ag7100_trc_new(nds_this_pkt,"nmbr ds for this pkt"); wmb(); mac->net_tx_packets ++; mac->net_tx_bytes += skb->len; ag7100_trc(ag7100_reg_rd(mac, AG7100_DMA_TX_CTRL),"dma idle"); ag7100_tx_start(mac); if (unlikely(ag7100_tx_ring_full(mac))) ag7100_handle_tx_full(mac); dev->trans_start = jiffies; return NETDEV_TX_OK;dropit: printk(MODULE_NAME ": dropping skb %08x\n", skb); kfree_skb(skb); return NETDEV_TX_OK;}/* * Interrupt handling: * - Recv NAPI style (refer to Documentation/networking/NAPI) * * 2 Rx interrupts: RX and Overflow (OVF). * - If we get RX and/or OVF, schedule a poll. Turn off _both_ interurpts. * * - When our poll's called, we * a) Have one or more packets to process and replenish * b) The hardware may have stopped because of an OVF. * * - We process and replenish as much as we can. For every rcvd pkt * indicated up the stack, the head moves. For every such slot that we * replenish with an skb, the tail moves. If head catches up with the tail * we're OOM. When all's done, we consider where we're at: * * if no OOM: * - if we're out of quota, let the ints be disabled and poll scheduled. * - If we've processed everything, enable ints and cancel poll. * * If OOM: * - Start a timer. Cancel poll. Ints still disabled. * If the hardware's stopped, no point in restarting yet. * * Note that in general, whether we're OOM or not, we still try to * indicate everything recvd, up. * * Locking: * The interrupt doesnt touch the ring => Rx is lockless * */static irqreturn_tag7100_intr(int cpl, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *)dev_id; ag7100_mac_t *mac = (ag7100_mac_t *)dev->priv; int isr, imr, handled = 0; isr = ag7100_get_isr(mac); imr = ag7100_reg_rd(mac, AG7100_DMA_INTR_MASK); ag7100_trc(isr,"isr"); ag7100_trc(imr,"imr"); assert(isr == (isr & imr)); if (likely(isr & (AG7100_INTR_RX | AG7100_INTR_RX_OVF))) { handled = 1; if (likely(netif_rx_schedule_prep(dev))) { ag7100_intr_disable_recv(mac); __netif_rx_schedule(dev); } else { printk(MODULE_NAME ": driver bug! interrupt while in poll\n"); assert(0); ag7100_intr_disable_recv(mac); } /*ag7100_recv_packets(dev, mac, 200, &budget);*/ } if (likely(isr & AG7100_INTR_TX)) { handled = 1; ag7100_intr_ack_tx(mac); ag7100_tx_reap(mac); } if (unlikely(isr & AG7100_INTR_RX_BUS_ERROR)) { assert(0); handled = 1; ag7100_intr_ack_rxbe(mac); } if (unlikely(isr & AG7100_INTR_TX_BUS_ERROR)) { assert(0); handled = 1; ag7100_intr_ack_txbe(mac); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -