📄 sunhme.c
字号:
while ((hme_read32(hp, bregs + BMAC_TXSWRESET) & 1) && --tries) udelay(20); /* Lettuce, tomato, buggy hardware (no extra charge)? */ if (!tries) printk(KERN_ERR "happy meal: Transceiver BigMac ATTACK!"); /* Take care. */ HMD(("done\n"));}static void happy_meal_rx_reset(struct happy_meal *hp, unsigned long bregs){ int tries = RX_RESET_TRIES; HMD(("happy_meal_rx_reset: reset, ")); /* We have a special on GNU/Viking hardware bugs today. */ hme_write32(hp, bregs + BMAC_RXSWRESET, 0); while ((hme_read32(hp, bregs + BMAC_RXSWRESET) & 1) && --tries) udelay(20); /* Will that be all? */ if (!tries) printk(KERN_ERR "happy meal: Receiver BigMac ATTACK!"); /* Don't forget your vik_1137125_wa. Have a nice day. */ HMD(("done\n"));}#define STOP_TRIES 16static void happy_meal_stop(struct happy_meal *hp, unsigned long gregs){ int tries = STOP_TRIES; HMD(("happy_meal_stop: reset, ")); /* We're consolidating our STB products, it's your lucky day. */ hme_write32(hp, gregs + GREG_SWRESET, GREG_RESET_ALL); while (hme_read32(hp, gregs + GREG_SWRESET) && --tries) udelay(20); /* Come back next week when we are "Sun Microelectronics". */ if (!tries) printk(KERN_ERR "happy meal: Fry guys."); /* Remember: "Different name, same old buggy as shit hardware." */ HMD(("done\n"));}static void happy_meal_get_counters(struct happy_meal *hp, unsigned long bregs){ struct net_device_stats *stats = &hp->net_stats; stats->rx_crc_errors += hme_read32(hp, bregs + BMAC_RCRCECTR); hme_write32(hp, bregs + BMAC_RCRCECTR, 0); stats->rx_frame_errors += hme_read32(hp, bregs + BMAC_UNALECTR); hme_write32(hp, bregs + BMAC_UNALECTR, 0); stats->rx_length_errors += hme_read32(hp, bregs + BMAC_GLECTR); hme_write32(hp, bregs + BMAC_GLECTR, 0); stats->tx_aborted_errors += hme_read32(hp, bregs + BMAC_EXCTR); stats->collisions += (hme_read32(hp, bregs + BMAC_EXCTR) + hme_read32(hp, bregs + BMAC_LTCTR)); hme_write32(hp, bregs + BMAC_EXCTR, 0); hme_write32(hp, bregs + BMAC_LTCTR, 0);}#if 0static void happy_meal_poll_start(struct happy_meal *hp, unsigned long tregs){ u32 tmp; int speed; ASD(("happy_meal_poll_start: ")); if (!(hp->happy_flags & HFLAG_POLLENABLE)) { HMD(("polling disabled, return\n")); return; } /* Start the MIF polling on the external transceiver. */ ASD(("polling on, ")); tmp = hme_read32(hp, tregs + TCVR_CFG); tmp &= ~(TCV_CFG_PDADDR | TCV_CFG_PREGADDR); tmp |= ((hp->paddr & 0x1f) << 10); tmp |= (TCV_PADDR_ETX << 3); tmp |= TCV_CFG_PENABLE; hme_write32(hp, tregs + TCVR_CFG, tmp); /* Let the bits set. */ udelay(200); /* We are polling now. */ ASD(("now polling, ")); hp->happy_flags |= HFLAG_POLL; /* Clear the poll flags, get the basic status as of now. */ hp->poll_flag = 0; hp->poll_data = hme_read32(hp, tregs + TCVR_STATUS) >> 16; if (hp->happy_flags & HFLAG_AUTO) speed = hp->auto_speed; else speed = hp->forced_speed; /* Listen only for the MIF interrupts we want to hear. */ ASD(("mif ints on, ")); if (speed == 100) hme_write32(hp, tregs + TCVR_IMASK, 0xfffb); else hme_write32(hp, tregs + TCVR_IMASK, 0xfff9); ASD(("done\n"));}#endifstatic void happy_meal_poll_stop(struct happy_meal *hp, unsigned long tregs){ ASD(("happy_meal_poll_stop: ")); /* If polling disabled or not polling already, nothing to do. */ if ((hp->happy_flags & (HFLAG_POLLENABLE | HFLAG_POLL)) != (HFLAG_POLLENABLE | HFLAG_POLL)) { HMD(("not polling, return\n")); return; } /* Shut up the MIF. */ ASD(("were polling, mif ints off, ")); hme_write32(hp, tregs + TCVR_IMASK, 0xffff); /* Turn off polling. */ ASD(("polling off, ")); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_PENABLE)); /* We are no longer polling. */ hp->happy_flags &= ~(HFLAG_POLL); /* Let the bits set. */ udelay(200); ASD(("done\n"));}/* Only Sun can take such nice parts and fuck up the programming interface * like this. Good job guys... */#define TCVR_RESET_TRIES 16 /* It should reset quickly */#define TCVR_UNISOLATE_TRIES 32 /* Dis-isolation can take longer. */static int happy_meal_tcvr_reset(struct happy_meal *hp, unsigned long tregs){ u32 tconfig; int result, tries = TCVR_RESET_TRIES; tconfig = hme_read32(hp, tregs + TCVR_CFG); ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig)); if (hp->tcvr_type == external) { ASD(("external<")); hme_write32(hp, tregs + TCVR_CFG, tconfig & ~(TCV_CFG_PSELECT)); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (result == TCVR_FAILURE) { ASD(("phyread_fail>\n")); return -1; } ASD(("phyread_ok,PSELECT>")); hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT); hp->tcvr_type = external; hp->paddr = TCV_PADDR_ETX; } else { if (tconfig & TCV_CFG_MDIO1) { ASD(("internal<PSELECT,")); hme_write32(hp, tregs + TCVR_CFG, (tconfig | TCV_CFG_PSELECT)); ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (result == TCVR_FAILURE) { ASD(("phyread_fail>\n")); return -1; } ASD(("phyread_ok,~PSELECT>")); hme_write32(hp, tregs + TCVR_CFG, (tconfig & ~(TCV_CFG_PSELECT))); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; } } ASD(("BMCR_RESET ")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, BMCR_RESET); while (--tries) { result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (result == TCVR_FAILURE) return -1; hp->sw_bmcr = result; if (!(result & BMCR_RESET)) break; udelay(20); } if (!tries) { ASD(("BMCR RESET FAILED!\n")); return -1; } ASD(("RESET_OK\n")); /* Get fresh copies of the PHY registers. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID1); hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID2); hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE); ASD(("UNISOLATE")); hp->sw_bmcr &= ~(BMCR_ISOLATE); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); tries = TCVR_UNISOLATE_TRIES; while (--tries) { result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (result == TCVR_FAILURE) return -1; if (!(result & BMCR_ISOLATE)) break; udelay(20); } if (!tries) { ASD((" FAILED!\n")); return -1; } ASD((" SUCCESS and CSCONFIG_DFBYPASS\n")); if (!is_lucent_phy(hp)) { result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, (result | CSCONFIG_DFBYPASS)); } return 0;}/* Figure out whether we have an internal or external transceiver. */static void happy_meal_transceiver_check(struct happy_meal *hp, unsigned long tregs){ unsigned long tconfig = hme_read32(hp, tregs + TCVR_CFG); ASD(("happy_meal_transceiver_check: tcfg=%08lx ", tconfig)); if (hp->happy_flags & HFLAG_POLL) { /* If we are polling, we must stop to get the transceiver type. */ ASD(("<polling> ")); if (hp->tcvr_type == internal) { if (tconfig & TCV_CFG_MDIO1) { ASD(("<internal> <poll stop> ")); happy_meal_poll_stop(hp, tregs); hp->paddr = TCV_PADDR_ETX; hp->tcvr_type = external; ASD(("<external>\n")); tconfig &= ~(TCV_CFG_PENABLE); tconfig |= TCV_CFG_PSELECT; hme_write32(hp, tregs + TCVR_CFG, tconfig); } } else { if (hp->tcvr_type == external) { ASD(("<external> ")); if (!(hme_read32(hp, tregs + TCVR_STATUS) >> 16)) { ASD(("<poll stop> ")); happy_meal_poll_stop(hp, tregs); hp->paddr = TCV_PADDR_ITX; hp->tcvr_type = internal; ASD(("<internal>\n")); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_PSELECT)); } ASD(("\n")); } else { ASD(("<none>\n")); } } } else { u32 reread = hme_read32(hp, tregs + TCVR_CFG); /* Else we can just work off of the MDIO bits. */ ASD(("<not polling> ")); if (reread & TCV_CFG_MDIO1) { hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT); hp->paddr = TCV_PADDR_ETX; hp->tcvr_type = external; ASD(("<external>\n")); } else { if (reread & TCV_CFG_MDIO0) { hme_write32(hp, tregs + TCVR_CFG, tconfig & ~(TCV_CFG_PSELECT)); hp->paddr = TCV_PADDR_ITX; hp->tcvr_type = internal; ASD(("<internal>\n")); } else { printk(KERN_ERR "happy meal: Transceiver and a coke please."); hp->tcvr_type = none; /* Grrr... */ ASD(("<none>\n")); } } }}/* The receive ring buffers are a bit tricky to get right. Here goes... * * The buffers we dma into must be 64 byte aligned. So we use a special * alloc_skb() routine for the happy meal to allocate 64 bytes more than * we really need. * * We use skb_reserve() to align the data block we get in the skb. We * also program the etxregs->cfg register to use an offset of 2. This * imperical constant plus the ethernet header size will always leave * us with a nicely aligned ip header once we pass things up to the * protocol layers. * * The numbers work out to: * * Max ethernet frame size 1518 * Ethernet header size 14 * Happy Meal base offset 2 * * Say a skb data area is at 0xf001b010, and its size alloced is * (ETH_FRAME_LEN + 64 + 2) = (1514 + 64 + 2) = 1580 bytes. * * First our alloc_skb() routine aligns the data base to a 64 byte * boundry. We now have 0xf001b040 as our skb data address. We * plug this into the receive descriptor address. * * Next, we skb_reserve() 2 bytes to account for the Happy Meal offset. * So now the data we will end up looking at starts at 0xf001b042. When * the packet arrives, we will check out the size received and subtract * this from the skb->length. Then we just pass the packet up to the * protocols as is, and allocate a new skb to replace this slot we have * just received from. * * The ethernet layer will strip the ether header from the front of the * skb we just sent to it, this leaves us with the ip header sitting * nicely aligned at 0xf001b050. Also, for tcp and udp packets the * Happy Meal has even checksummed the tcp/udp data for us. The 16 * bit checksum is obtained from the low bits of the receive descriptor * flags, thus: * * skb->csum = rxd->rx_flags & 0xffff; * skb->ip_summed = CHECKSUM_HW; * * before sending off the skb to the protocols, and we are good as gold. */static void happy_meal_clean_rings(struct happy_meal *hp){ int i; for (i = 0; i < RX_RING_SIZE; i++) { if (hp->rx_skbs[i] != NULL) { struct sk_buff *skb = hp->rx_skbs[i]; struct happy_meal_rxd *rxd; u32 dma_addr; rxd = &hp->happy_block->happy_meal_rxd[i]; dma_addr = hme_read_desc32(hp, &rxd->rx_addr); hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE); dev_kfree_skb_any(skb); hp->rx_skbs[i] = NULL; } } for (i = 0; i < TX_RING_SIZE; i++) { if (hp->tx_skbs[i] != NULL) { struct sk_buff *skb = hp->tx_skbs[i]; struct happy_meal_txd *txd; u32 dma_addr; txd = &hp->happy_block->happy_meal_txd[i]; dma_addr = hme_read_desc32(hp, &txd->tx_addr); hme_dma_unmap(hp, dma_addr, skb->len, DMA_TODEVICE); dev_kfree_skb_any(skb); hp->tx_skbs[i] = NULL; } }}static void happy_meal_init_rings(struct happy_meal *hp, int from_irq){ struct hmeal_init_block *hb = hp->happy_block; struct net_device *dev = hp->dev; int i, gfp_flags = GFP_KERNEL; if (from_irq || in_interrupt()) gfp_flags = GFP_ATOMIC; HMD(("happy_meal_init_rings: counters to zero, ")); hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0; /* Free any skippy bufs left around in the rings. */ HMD(("clean, ")); happy_meal_clean_rings(hp); /* Now get new skippy bufs for the receive ring. */ HMD(("init rxring, ")); for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); if (!skb) { hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0); continue; } hp->rx_skbs[i] = skb; skb->dev = dev; /* Because we reserve afterwards. */ skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET)); hme_write_rxd(hp, &hb->happy_meal_rxd[i], (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)), hme_dma_map(hp, skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE)); skb_reserve(skb, RX_OFFSET); } HMD(("init txring, ")); for (i = 0; i < TX_RING_SIZE; i++) hme_write_txd(hp, &hb->happy_meal_txd[i], 0, 0); HMD(("done\n"));}static void happy_meal_begin_auto_negotiation(struct happy_meal *hp, unsigned long tregs, struct ethtool_cmd *ep){ int timeout; /* Read all of the registers we are interested in now. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID1); hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID2); /* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */ hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE); if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { /* Advertise everything we can support. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -