📄 gmac.c
字号:
return -1;}/* * Set the MAC duplex mode. * * Side effect: stops Tx MAC */static voidgmac_set_duplex_mode(struct gmac *gm, int full_duplex){ /* Stop Tx MAC */ GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); while(GM_IN(GM_MAC_TX_CONFIG) & GM_MAC_TX_CONF_ENABLE) ; if (full_duplex) { GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_IGNORE_CARRIER | GM_MAC_TX_CONF_IGNORE_COLL); GM_BIC(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_DISABLE_ECHO); } else { GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_IGNORE_CARRIER | GM_MAC_TX_CONF_IGNORE_COLL); GM_BIS(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_DISABLE_ECHO); }}/* Set the MAC gigabit mode. Side effect: stops Tx MAC */static voidgmac_set_gigabit_mode(struct gmac *gm, int gigabit){ /* Stop Tx MAC */ GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); while(GM_IN(GM_MAC_TX_CONFIG) & GM_MAC_TX_CONF_ENABLE) ; if (gigabit) { GM_BIS(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE); } else { GM_BIC(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE); }}/* * Initialize a bunch of registers to put the chip into a known * and hopefully happy state */static voidgmac_mac_init(struct gmac *gm, unsigned char *mac_addr){ int i, fifo_size; /* Set random seed to low bits of MAC address */ GM_OUT(GM_MAC_RANDOM_SEED, mac_addr[5] | (mac_addr[4] << 8)); /* Configure the data path mode to MII/GII */ GM_OUT(GM_PCS_DATAPATH_MODE, GM_PCS_DATAPATH_MII); /* Configure XIF to MII mode. Full duplex led is set * by Apple, so... */ GM_OUT(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_TX_MII_OUT_EN | GM_MAC_XIF_CONF_FULL_DPLX_LED); /* Mask out all MAC interrupts */ GM_OUT(GM_MAC_TX_MASK, 0xffff); GM_OUT(GM_MAC_RX_MASK, 0xffff); GM_OUT(GM_MAC_CTRLSTAT_MASK, 0xff); /* Setup bits of MAC */ GM_OUT(GM_MAC_SND_PAUSE, GM_MAC_SND_PAUSE_DEFAULT); GM_OUT(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_RCV_PAUSE_EN); /* Configure GEM DMA */ GM_OUT(GM_GCONF, GM_GCONF_BURST_SZ | (31 << GM_GCONF_TXDMA_LIMIT_SHIFT) | (31 << GM_GCONF_RXDMA_LIMIT_SHIFT)); GM_OUT(GM_TX_CONF, (GM_TX_CONF_FIFO_THR_DEFAULT << GM_TX_CONF_FIFO_THR_SHIFT) | NTX_CONF); /* 34 byte offset for checksum computation. This works because ip_input() will clear out * the skb->csum and skb->ip_summed fields and recompute the csum if IP options are * present in the header. 34 == (ethernet header len) + sizeof(struct iphdr) */ GM_OUT(GM_RX_CONF, (RX_OFFSET << GM_RX_CONF_FBYTE_OFF_SHIFT) | (0x22 << GM_RX_CONF_CHK_START_SHIFT) | (GM_RX_CONF_DMA_THR_DEFAULT << GM_RX_CONF_DMA_THR_SHIFT) | NRX_CONF); /* Configure other bits of MAC */ GM_OUT(GM_MAC_INTR_PKT_GAP0, GM_MAC_INTR_PKT_GAP0_DEFAULT); GM_OUT(GM_MAC_INTR_PKT_GAP1, GM_MAC_INTR_PKT_GAP1_DEFAULT); GM_OUT(GM_MAC_INTR_PKT_GAP2, GM_MAC_INTR_PKT_GAP2_DEFAULT); GM_OUT(GM_MAC_MIN_FRAME_SIZE, GM_MAC_MIN_FRAME_SIZE_DEFAULT); GM_OUT(GM_MAC_MAX_FRAME_SIZE, GM_MAC_MAX_FRAME_SIZE_DEFAULT); GM_OUT(GM_MAC_PREAMBLE_LEN, GM_MAC_PREAMBLE_LEN_DEFAULT); GM_OUT(GM_MAC_JAM_SIZE, GM_MAC_JAM_SIZE_DEFAULT); GM_OUT(GM_MAC_ATTEMPT_LIMIT, GM_MAC_ATTEMPT_LIMIT_DEFAULT); GM_OUT(GM_MAC_SLOT_TIME, GM_MAC_SLOT_TIME_DEFAULT); GM_OUT(GM_MAC_CONTROL_TYPE, GM_MAC_CONTROL_TYPE_DEFAULT); /* Setup MAC addresses, clear filters, clear hash table */ GM_OUT(GM_MAC_ADDR_NORMAL0, (mac_addr[4] << 8) + mac_addr[5]); GM_OUT(GM_MAC_ADDR_NORMAL1, (mac_addr[2] << 8) + mac_addr[3]); GM_OUT(GM_MAC_ADDR_NORMAL2, (mac_addr[0] << 8) + mac_addr[1]); GM_OUT(GM_MAC_ADDR_ALT0, 0); GM_OUT(GM_MAC_ADDR_ALT1, 0); GM_OUT(GM_MAC_ADDR_ALT2, 0); GM_OUT(GM_MAC_ADDR_CTRL0, 0x0001); GM_OUT(GM_MAC_ADDR_CTRL1, 0xc200); GM_OUT(GM_MAC_ADDR_CTRL2, 0x0180); GM_OUT(GM_MAC_ADDR_FILTER0, 0); GM_OUT(GM_MAC_ADDR_FILTER1, 0); GM_OUT(GM_MAC_ADDR_FILTER2, 0); GM_OUT(GM_MAC_ADDR_FILTER_MASK1_2, 0); GM_OUT(GM_MAC_ADDR_FILTER_MASK0, 0); for (i = 0; i < 27; ++i) GM_OUT(GM_MAC_ADDR_FILTER_HASH0 + i, 0); /* Clear stat counters */ GM_OUT(GM_MAC_COLLISION_CTR, 0); GM_OUT(GM_MAC_FIRST_COLLISION_CTR, 0); GM_OUT(GM_MAC_EXCS_COLLISION_CTR, 0); GM_OUT(GM_MAC_LATE_COLLISION_CTR, 0); GM_OUT(GM_MAC_DEFER_TIMER_COUNTER, 0); GM_OUT(GM_MAC_PEAK_ATTEMPTS, 0); GM_OUT(GM_MAC_RX_FRAME_CTR, 0); GM_OUT(GM_MAC_RX_LEN_ERR_CTR, 0); GM_OUT(GM_MAC_RX_ALIGN_ERR_CTR, 0); GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0); GM_OUT(GM_MAC_RX_CODE_VIOLATION_CTR, 0); /* default to half duplex */ GM_OUT(GM_MAC_TX_CONFIG, 0); GM_OUT(GM_MAC_RX_CONFIG, 0); gmac_set_duplex_mode(gm, gm->full_duplex); /* Setup pause thresholds */ fifo_size = GM_IN(GM_RX_FIFO_SIZE); GM_OUT(GM_RX_PTH, ((fifo_size - ((GM_MAC_MAX_FRAME_SIZE_ALIGN + 8) * 2 / GM_RX_PTH_UNITS)) << GM_RX_PTH_OFF_SHIFT) | ((fifo_size - ((GM_MAC_MAX_FRAME_SIZE_ALIGN + 8) * 3 / GM_RX_PTH_UNITS)) << GM_RX_PTH_ON_SHIFT)); /* Setup interrupt blanking */ if (GM_IN(GM_BIF_CFG) & GM_BIF_CFG_M66EN) GM_OUT(GM_RX_BLANK, (5 << GM_RX_BLANK_INTR_PACKETS_SHIFT) | (8 << GM_RX_BLANK_INTR_TIME_SHIFT)); else GM_OUT(GM_RX_BLANK, (5 << GM_RX_BLANK_INTR_PACKETS_SHIFT) | (4 << GM_RX_BLANK_INTR_TIME_SHIFT)); }/* * Fill the Rx and Tx rings with good initial values, alloc * fresh Rx skb's. */static voidgmac_init_rings(struct gmac *gm, int from_irq){ int i; struct sk_buff *skb; unsigned char *data; struct gmac_dma_desc *ring; int gfp_flags = GFP_KERNEL; if (from_irq || in_interrupt()) gfp_flags = GFP_ATOMIC; /* init rx ring */ ring = (struct gmac_dma_desc *) gm->rxring; memset(ring, 0, NRX * sizeof(struct gmac_dma_desc)); for (i = 0; i < NRX; ++i, ++ring) { data = dummy_buf; gm->rx_buff[i] = skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); if (skb != 0) { skb->dev = gm->dev; skb_put(skb, ETH_FRAME_LEN + RX_OFFSET); skb_reserve(skb, RX_OFFSET); data = skb->data - RX_OFFSET; } st_le32(&ring->lo_addr, virt_to_bus(data)); st_le32(&ring->size, RX_SZ_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET) << RX_SZ_SHIFT)); } /* init tx ring */ ring = (struct gmac_dma_desc *) gm->txring; memset(ring, 0, NTX * sizeof(struct gmac_dma_desc)); gm->next_rx = 0; gm->next_tx = 0; gm->tx_gone = 0; /* set pointers in chip */ mb(); GM_OUT(GM_RX_DESC_HI, 0); GM_OUT(GM_RX_DESC_LO, virt_to_bus(gm->rxring)); GM_OUT(GM_TX_DESC_HI, 0); GM_OUT(GM_TX_DESC_LO, virt_to_bus(gm->txring));}/* * Start the Tx and Rx DMA engines and enable interrupts * * Note: The various mdelay(20); come from Darwin implentation. Some * tests (doc ?) are needed to replace those with something more intrusive. */static voidgmac_start_dma(struct gmac *gm){ /* Enable Tx and Rx */ GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN); mdelay(20); GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN); mdelay(20); GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE); mdelay(20); GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); mdelay(20); /* Kick the receiver and enable interrupts */ GM_OUT(GM_RX_KICK, NRX); GM_BIC(GM_IRQ_MASK, GM_IRQ_TX_INT_ME | GM_IRQ_TX_ALL | GM_IRQ_RX_DONE | GM_IRQ_RX_TAG_ERR | GM_IRQ_MAC_RX | GM_IRQ_MIF | GM_IRQ_BUS_ERROR);}/* * Stop the Tx and Rx DMA engines after disabling interrupts * * Note: The various mdelay(20); come from Darwin implentation. Some * tests (doc ?) are needed to replace those with something more intrusive. */static voidgmac_stop_dma(struct gmac *gm){ /* disable interrupts */ GM_OUT(GM_IRQ_MASK, 0xffffffff); /* Enable Tx and Rx */ GM_BIC(GM_TX_CONF, GM_TX_CONF_DMA_EN); mdelay(20); GM_BIC(GM_RX_CONF, GM_RX_CONF_DMA_EN); mdelay(20); GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE); mdelay(20); GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); mdelay(20);}/* * Configure promisc mode and setup multicast hash table * filter */#define CRC_POLY 0xedb88320static voidgmac_set_multicast(struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; struct dev_mc_list *dmi = dev->mc_list; int i,j,k,b; unsigned long crc; int multicast_hash = 0; int multicast_all = 0; int promisc = 0; /* Lock out others. */ netif_stop_queue(dev); if (dev->flags & IFF_PROMISC) promisc = 1; else if ((dev->flags & IFF_ALLMULTI) /* || (dev->mc_count > XXX) */) { multicast_all = 1; } else { u16 hash_table[16]; for(i = 0; i < 16; i++) hash_table[i] = 0; for (i = 0; i < dev->mc_count; i++) { crc = ~0; for (j = 0; j < 6; ++j) { b = dmi->dmi_addr[j]; for (k = 0; k < 8; ++k) { if ((crc ^ b) & 1) crc = (crc >> 1) ^ CRC_POLY; else crc >>= 1; b >>= 1; } } j = crc >> 24; /* bit number in multicast_filter */ hash_table[j >> 4] |= 1 << (15 - (j & 0xf)); dmi = dmi->next; } for (i = 0; i < 16; i++) GM_OUT(GM_MAC_ADDR_FILTER_HASH0 + (i*4), hash_table[i]); GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); multicast_hash = 1; } if (promisc) GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL); else GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL); if (multicast_hash) GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); else GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); if (multicast_all) GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL_MULTI); else GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL_MULTI); /* Let us get going again. */ netif_wake_queue(dev);}/* * Open the interface */static intgmac_open(struct net_device *dev){ int ret; struct gmac *gm = (struct gmac *) dev->priv; /* Power up and reset chip */ if (gmac_powerup_and_reset(dev)) return -EIO; /* Get our interrupt */ ret = request_irq(dev->irq, gmac_interrupt, 0, dev->name, dev); if (ret) { printk(KERN_ERR "%s can't get irq %d\n", dev->name, dev->irq); return ret; } gm->full_duplex = 0; gm->phy_status = 0; /* Find a PHY */ if (!mii_lookup_and_reset(gm)) printk(KERN_WARNING "%s WARNING ! Can't find PHY\n", dev->name); /* Configure the PHY */ mii_setup_phy(gm); /* Initialize the descriptor rings */ gmac_init_rings(gm, 0); /* Initialize the MAC */ gmac_mac_init(gm, dev->dev_addr); /* Initialize the multicast tables & promisc mode if any */ gmac_set_multicast(dev); /* * Check out PHY status and start auto-poll * * Note: do this before enabling interrutps */ mii_interrupt(gm); /* Start the chip */ gmac_start_dma(gm); gm->opened = 1; return 0;}/* * Close the interface */static intgmac_close(struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; int i; gm->opened = 0; /* Stop chip and interrupts */ gmac_stop_dma(gm); /* Stop polling PHY */ mii_poll_stop(gm); /* Free interrupt */ free_irq(dev->irq, dev); /* Shut down chip */ gmac_set_power(gm, 0); /* Empty rings of any remaining gremlins */ for (i = 0; i < NRX; ++i) { if (gm->rx_buff[i] != 0) { dev_kfree_skb(gm->rx_buff[i]); gm->rx_buff[i] = 0; } } for (i = 0; i < NTX; ++i) { if (gm->tx_buff[i] != 0) { dev_kfree_skb(gm->tx_buff[i]); gm->tx_buff[i] = 0; } } return 0;}#ifdef CONFIG_PMAC_PBOOKintgmac_sleep_notify(struct pmu_sleep_notifier *self, int when){ struct gmac *gm; int i; /* XXX should handle more than one */ if (gmacs == NULL) return PBOOK_SLEEP_OK; gm = (struct gmac *) gmacs->priv; if (!gm->opened) return PBOOK_SLEEP_OK; switch (when) { case PBOOK_SLEEP_REQUEST: break; case PBOOK_SLEEP_REJECT: break; case PBOOK_SLEEP_NOW: disable_irq(gm->dev->irq); netif_stop_queue(gm->dev); gmac_stop_dma(gm); mii_poll_stop(gm); gmac_set_power(gm, 0); for (i = 0; i < NRX; ++i) { if (gm->rx_buff[i] != 0) { dev_kfree_skb(gm->rx_buff[i]); gm->rx_buff[i] = 0; } } for (i = 0; i < NTX; ++i) { if (gm->tx_buff[i] != 0) { dev_kfree_skb(gm->tx_buff[i]); gm->tx_buff[i] = 0; } } break; case PBOOK_WAKE: /* see if this is enough */ gmac_powerup_and_reset(gm->dev); gm->full_duplex = 0; gm->phy_status = 0; mii_lookup_and_reset(gm); mii_setup_phy(gm); gmac_init_rings(gm, 0); gmac_mac_init(gm, gm->dev->dev_addr); gmac_set_multicast(gm->dev); mii_interrupt(gm); gmac_start_dma(gm); netif_start_queue(gm->dev); enable_irq(gm->dev->irq); break; } return PBOOK_SLEEP_OK;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -