📄 sunhme.c
字号:
return 0;}/* Figure out whether we have an internal or external transceiver. */static void happy_meal_transceiver_check(struct happy_meal *hp, struct hmeal_tcvregs *tregs){ unsigned long tconfig = hme_read32(hp, &tregs->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->cfg, tconfig); } } else { if(hp->tcvr_type == external) { ASD(("<external> ")); if(!(hme_read32(hp, &tregs->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->cfg, hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_PSELECT)); } ASD(("\n")); } else { ASD(("<none>\n")); } } } else { unsigned long reread = hme_read32(hp, &tregs->cfg); /* Else we can just work off of the MDIO bits. */ ASD(("<not polling> ")); if(reread & TCV_CFG_MDIO1) { hme_write32(hp, &tregs->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->cfg, tconfig & ~(TCV_CFG_PSELECT)); hp->paddr = TCV_PADDR_ITX; hp->tcvr_type = internal; ASD(("<internal>\n")); } else { printk("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 inline 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) { dev_kfree_skb(hp->rx_skbs[i]); hp->rx_skbs[i] = NULL; } } for(i = 0; i < TX_RING_SIZE; i++) { if(hp->tx_skbs[i] != NULL) { dev_kfree_skb(hp->tx_skbs[i]); 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 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 | GFP_DMA); if(!skb) continue; hp->rx_skbs[i] = skb; skb->dev = dev; /* Because we reserve afterwards. */ skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));#ifdef CONFIG_PCI if(hp->happy_flags & HFLAG_PCI) { pcihme_write_rxd(&hb->happy_meal_rxd[i], (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), (u32)virt_to_bus((volatile void *)skb->data)); } else#endif#ifndef __sparc_v9__ if (sparc_cpu_model == sun4d) { __u32 va = (__u32)hp->sun4d_buffers + i * PAGE_SIZE; hb->happy_meal_rxd[i].rx_addr = iounit_map_dma_page(va, skb->data, hp->happy_sbus_dev->my_bus); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); } else#endif { hb->happy_meal_rxd[i].rx_addr = sbus_dvma_addr(skb->data); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); } skb_reserve(skb, RX_OFFSET); } HMD(("init txring, ")); for(i = 0; i < TX_RING_SIZE; i++) hb->happy_meal_txd[i].tx_flags = 0; HMD(("done\n"));}#ifndef __sparc_v9__static void sun4c_happy_meal_init_rings(struct happy_meal *hp){ struct hmeal_init_block *hb = hp->happy_block; __u32 hbufs = hp->s4c_buf_dvma; int i; HMD(("happy_meal_init_rings: counters to zero, ")); hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0; HMD(("init rxring, ")); for(i = 0; i < RX_RING_SIZE; i++) { hb->happy_meal_rxd[i].rx_addr = hbufs + hbuf_offset(rx_buf, i); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((SUN4C_RX_BUFF_SIZE - RX_OFFSET) << 16)); } HMD(("init txring, ")); for(i = 0; i < TX_RING_SIZE; i++) hb->happy_meal_txd[i].tx_flags = 0; HMD(("done\n"));}#endifstatic void happy_meal_begin_auto_negotiation(struct happy_meal *hp, struct hmeal_tcvregs *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. */ if(hp->sw_bmsr & BMSR_10HALF) hp->sw_advertise |= (ADVERTISE_10HALF); else hp->sw_advertise &= ~(ADVERTISE_10HALF); if(hp->sw_bmsr & BMSR_10FULL) hp->sw_advertise |= (ADVERTISE_10FULL); else hp->sw_advertise &= ~(ADVERTISE_10FULL); if(hp->sw_bmsr & BMSR_100HALF) hp->sw_advertise |= (ADVERTISE_100HALF); else hp->sw_advertise &= ~(ADVERTISE_100HALF); if(hp->sw_bmsr & BMSR_100FULL) hp->sw_advertise |= (ADVERTISE_100FULL); else hp->sw_advertise &= ~(ADVERTISE_100FULL); happy_meal_tcvr_write(hp, tregs, DP83840_ADVERTISE, hp->sw_advertise); /* XXX Currently no Happy Meal cards I know off support 100BaseT4, * XXX and this is because the DP83840 does not support it, changes * XXX would need to be made to the tx/rx logic in the driver as well * XXX so I completely skip checking for it in the BMSR for now. */#ifdef AUTO_SWITCH_DEBUG ASD(("%s: Advertising [ ", hp->dev->name)); if(hp->sw_advertise & ADVERTISE_10HALF) ASD(("10H ")); if(hp->sw_advertise & ADVERTISE_10FULL) ASD(("10F ")); if(hp->sw_advertise & ADVERTISE_100HALF) ASD(("100H ")); if(hp->sw_advertise & ADVERTISE_100FULL) ASD(("100F "));#endif /* Enable Auto-Negotiation, this is usually on already... */ hp->sw_bmcr |= BMCR_ANENABLE; happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); /* Restart it to make sure it is going. */ hp->sw_bmcr |= BMCR_ANRESTART; happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); /* BMCR_ANRESTART self clears when the process has begun. */ timeout = 64; /* More than enough. */ while(--timeout) { hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if(!(hp->sw_bmcr & BMCR_ANRESTART)) break; /* got it. */ udelay(10); } if(!timeout) { printk("%s: Happy Meal would not start auto negotiation " "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr); printk("%s: Performing force link detection.\n", hp->dev->name); goto force_link; } else { hp->timer_state = arbwait; } } else {force_link: /* Force the link up, trying first a particular mode. * Either we are here at the request of ethtool or * because the Happy Meal would not start to autoneg. */ /* Disable auto-negotiation in BMCR, enable the duplex and * speed setting, init the timer state machine, and fire it off. */ if(ep == NULL || ep->autoneg == AUTONEG_ENABLE) { hp->sw_bmcr = BMCR_SPEED100; } else { if(ep->speed == SPEED_100) hp->sw_bmcr = BMCR_SPEED100; else hp->sw_bmcr = 0; if(ep->duplex == DUPLEX_FULL) hp->sw_bmcr |= BMCR_FULLDPLX; } happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); /* OK, seems we need do disable the transceiver for the first * tick to make sure we get an accurate link state at the * second tick. */ hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); hp->timer_state = ltrywait; } hp->timer_ticks = 0; hp->happy_timer.expires = jiffies + (12 * HZ)/10; /* 1.2 sec. */ hp->happy_timer.data = (unsigned long) hp; hp->happy_timer.function = &happy_meal_timer; add_timer(&hp->happy_timer);}#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */static int happy_meal_init(struct happy_meal *hp, int from_irq){ struct hmeal_gregs *gregs = hp->gregs; struct hmeal_etxregs *etxregs = hp->etxregs; struct hmeal_erxregs *erxregs = hp->erxregs; struct hmeal_bigmacregs *bregs = hp->bigmacregs; struct hmeal_tcvregs *tregs = hp->tcvregs; unsigned long regtmp, rxcfg; unsigned char *e = &hp->dev->dev_addr[0]; /* If auto-negotiation timer is running, kill it. */ del_timer(&hp->happy_timer); HMD(("happy_meal_init: happy_flags[%08x] ", hp->happy_flags)); if(!(hp->happy_flags & HFLAG_INIT)) { HMD(("set HFLAG_INIT, ")); hp->happy_flags |= HFLAG_INIT; happy_meal_get_counters(hp, bregs); } /* Stop polling. */ HMD(("to happy_meal_poll_stop\n")); happy_meal_poll_stop(hp, tregs); /* Stop transmitter and receiver. */ HMD(("happy_meal_init: to happy_meal_stop\n")); happy_meal_stop(hp, gregs); /* Alloc and reset the tx/rx descriptor chains. */ HMD(("happy_meal_init: to happy_meal_init_rings\n"));#ifndef __sparc_v9__ if(sparc_cpu_model == sun4c) sun4c_happy_meal_init_rings(hp); else#endif happy_meal_init_rings(hp, from_irq); /* Shut up the MIF. */ HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ", hme_read32(hp, &tregs->int_mask))); hme_write32(hp, &tregs->int_mask, 0xffff); /* See if we can enable the MIF frame on this card to speak to the DP83840. */ if(hp->happy_flags & HFLAG_FENABLE) { HMD(("use frame old[%08x], ", hme_read32(hp, &tregs->cfg))); hme_write32(hp, &tregs->cfg, hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_BENABLE)); } else { HMD(("use bitbang old[%08x], ", hme_read32(hp, &tregs->cfg))); hme_write32(hp, &tregs->cfg, hme_read32(hp, &tregs->cfg) | TCV_CFG_BENABLE); } /* Check the state of the transceiver. */ HMD(("to happy_meal_transceiver_check\n")); happy_meal_transceiver_check(hp, tregs); /* Put the Big Mac into a sane state. */ HMD(("happy_meal_init: ")); switch(hp->tcvr_type) { case none: /* Cannot operate if we don't know the transceiver type! */ HMD(("AAIEEE no transceiver type, EAGAIN")); return -EAGAIN; case internal: /* Using the MII buffers. */ HMD(("internal, using MII, ")); hme_write32(hp, &bregs->xif_cfg, 0); break; case external: /* Not using the MII, disable it. */ HMD(("external, disable MII, ")); hme_write32(hp, &bregs->xif_cfg, BIGMAC_XCFG_MIIDISAB); break; }; if(happy_meal_tcvr_reset(hp, tregs)) return -EAGAIN; /* Reset the Happy Meal Big Mac transceiver and the receiver. */ HMD(("tx/rx reset, ")); happy_meal_tx_reset(hp, bregs); happy_meal_rx_reset(hp, bregs); /* Set jam size and inter-packet gaps to reasonable defaults. */ HMD(("jsize/ipg1/ipg2, ")); hme_write32(hp, &bregs->jsize, DEFAULT_JAMSIZE); hme_write32(hp, &bregs->ipkt_gap1, DEFAULT_IPG1); hme_write32(hp, &bregs->ipkt_gap2, DEFAULT_IPG2); /* Load up the MAC address and random seed. */ HMD(("rseed/macaddr, ")); /* The docs recommend to use the 10LSB of our MAC here. */ hme_write32(hp, &bregs->rand_seed, ((e[5] | e[4]<<8)&0x3ff)); hme_write32(hp, &bregs->mac_addr2, ((e[4] << 8) | e[5])); hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3])); hme_write32(hp, &bregs->mac_addr0, ((e[0] << 8) | e[1])); HMD(("htable, ")); if((hp->dev->flags & IFF_ALLMULTI) || (hp->dev->mc_count > 64)) { hme_write32(hp, &bregs->htable0, 0xffff); hme_write32(hp, &bregs->htable1, 0xffff); hme_write32(hp, &bregs->htable2, 0xffff); hme_write32(hp, &bregs->htable3, 0xffff); } else if((hp->dev->flags & IFF_PROMISC) == 0) { u16 hash_table[4]; struct dev_mc_list *dmi = hp->dev->mc_list;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -