diff options
author | Jens Axboe <jens.axboe@oracle.com> | 2010-05-21 21:27:26 +0200 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2010-05-21 21:27:26 +0200 |
commit | ee9a3607fb03e804ddf624544105f4e34260c380 (patch) | |
tree | ce41b6e0fa10982a306f6c142a92dbf3c9961284 /drivers/net/bfin_mac.c | |
parent | pipe: set lower and upper limit on max pages in the pipe page array (diff) | |
parent | Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jac... (diff) | |
download | linux-ee9a3607fb03e804ddf624544105f4e34260c380.tar.xz linux-ee9a3607fb03e804ddf624544105f4e34260c380.zip |
Merge branch 'master' into for-2.6.35
Conflicts:
fs/ext3/fsync.c
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers/net/bfin_mac.c')
-rw-r--r-- | drivers/net/bfin_mac.c | 559 |
1 files changed, 515 insertions, 44 deletions
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 587f93cf03f6..39a54bad397f 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -33,6 +33,7 @@ #include <asm/dma.h> #include <linux/dma-mapping.h> +#include <asm/div64.h> #include <asm/dpmc.h> #include <asm/blackfin.h> #include <asm/cacheflush.h> @@ -80,9 +81,6 @@ static u16 pin_req[] = P_RMII0; static u16 pin_req[] = P_MII0; #endif -static void bfin_mac_disable(void); -static void bfin_mac_enable(void); - static void desc_list_free(void) { struct net_dma_desc_rx *r; @@ -202,6 +200,11 @@ static int desc_list_init(void) goto init_error; } skb_reserve(new_skb, NET_IP_ALIGN); + /* Invidate the data cache of skb->data range when it is write back + * cache. It will prevent overwritting the new data from DMA + */ + blackfin_dcache_invalidate_range((unsigned long)new_skb->head, + (unsigned long)new_skb->end); r->skb = new_skb; /* @@ -254,7 +257,7 @@ init_error: * MII operations */ /* Wait until the previous MDC/MDIO transaction has completed */ -static void bfin_mdio_poll(void) +static int bfin_mdio_poll(void) { int timeout_cnt = MAX_TIMEOUT_CNT; @@ -264,22 +267,30 @@ static void bfin_mdio_poll(void) if (timeout_cnt-- < 0) { printk(KERN_ERR DRV_NAME ": wait MDC/MDIO transaction to complete timeout\n"); - break; + return -ETIMEDOUT; } } + + return 0; } /* Read an off-chip register in a PHY through the MDC/MDIO port */ static int bfin_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { - bfin_mdio_poll(); + int ret; + + ret = bfin_mdio_poll(); + if (ret) + return ret; /* read mode */ bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) | SET_REGAD((u16) regnum) | STABUSY); - bfin_mdio_poll(); + ret = bfin_mdio_poll(); + if (ret) + return ret; return (int) bfin_read_EMAC_STADAT(); } @@ -288,7 +299,11 @@ static int bfin_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value) { - bfin_mdio_poll(); + int ret; + + ret = bfin_mdio_poll(); + if (ret) + return ret; bfin_write_EMAC_STADAT((u32) value); @@ -298,9 +313,7 @@ static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, STAOP | STABUSY); - bfin_mdio_poll(); - - return 0; + return bfin_mdio_poll(); } static int bfin_mdiobus_reset(struct mii_bus *bus) @@ -458,6 +471,14 @@ static int mii_probe(struct net_device *dev) * Ethtool support */ +/* + * interrupt routine for magic packet wakeup + */ +static irqreturn_t bfin_mac_wake_interrupt(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + static int bfin_mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) { @@ -492,11 +513,57 @@ static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev, strcpy(info->bus_info, dev_name(&dev->dev)); } +static void bfin_mac_ethtool_getwol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct bfin_mac_local *lp = netdev_priv(dev); + + wolinfo->supported = WAKE_MAGIC; + wolinfo->wolopts = lp->wol; +} + +static int bfin_mac_ethtool_setwol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct bfin_mac_local *lp = netdev_priv(dev); + int rc; + + if (wolinfo->wolopts & (WAKE_MAGICSECURE | + WAKE_UCAST | + WAKE_MCAST | + WAKE_BCAST | + WAKE_ARP)) + return -EOPNOTSUPP; + + lp->wol = wolinfo->wolopts; + + if (lp->wol && !lp->irq_wake_requested) { + /* register wake irq handler */ + rc = request_irq(IRQ_MAC_WAKEDET, bfin_mac_wake_interrupt, + IRQF_DISABLED, "EMAC_WAKE", dev); + if (rc) + return rc; + lp->irq_wake_requested = true; + } + + if (!lp->wol && lp->irq_wake_requested) { + free_irq(IRQ_MAC_WAKEDET, dev); + lp->irq_wake_requested = false; + } + + /* Make sure the PHY driver doesn't suspend */ + device_init_wakeup(&dev->dev, lp->wol); + + return 0; +} + static const struct ethtool_ops bfin_mac_ethtool_ops = { .get_settings = bfin_mac_ethtool_getsettings, .set_settings = bfin_mac_ethtool_setsettings, .get_link = ethtool_op_get_link, .get_drvinfo = bfin_mac_ethtool_getdrvinfo, + .get_wol = bfin_mac_ethtool_getwol, + .set_wol = bfin_mac_ethtool_setwol, }; /**************************************************************************/ @@ -509,10 +576,11 @@ void setup_system_regs(struct net_device *dev) * Configure checksum support and rcve frame word alignment */ sysctl = bfin_read_EMAC_SYSCTL(); + sysctl |= RXDWA; #if defined(BFIN_MAC_CSUM_OFFLOAD) - sysctl |= RXDWA | RXCKS; + sysctl |= RXCKS; #else - sysctl |= RXDWA; + sysctl &= ~RXCKS; #endif bfin_write_EMAC_SYSCTL(sysctl); @@ -551,6 +619,309 @@ static int bfin_mac_set_mac_address(struct net_device *dev, void *p) return 0; } +#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP +#define bfin_mac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE) + +static int bfin_mac_hwtstamp_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct bfin_mac_local *lp = netdev_priv(netdev); + u16 ptpctl; + u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n", + __func__, config.flags, config.tx_type, config.rx_filter); + + /* reserved for future extensions */ + if (config.flags) + return -EINVAL; + + if ((config.tx_type != HWTSTAMP_TX_OFF) && + (config.tx_type != HWTSTAMP_TX_ON)) + return -ERANGE; + + ptpctl = bfin_read_EMAC_PTP_CTL(); + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + /* + * Dont allow any timestamping + */ + ptpfv3 = 0xFFFFFFFF; + bfin_write_EMAC_PTP_FV3(ptpfv3); + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + /* + * Clear the five comparison mask bits (bits[12:8]) in EMAC_PTP_CTL) + * to enable all the field matches. + */ + ptpctl &= ~0x1F00; + bfin_write_EMAC_PTP_CTL(ptpctl); + /* + * Keep the default values of the EMAC_PTP_FOFF register. + */ + ptpfoff = 0x4A24170C; + bfin_write_EMAC_PTP_FOFF(ptpfoff); + /* + * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2 + * registers. + */ + ptpfv1 = 0x11040800; + bfin_write_EMAC_PTP_FV1(ptpfv1); + ptpfv2 = 0x0140013F; + bfin_write_EMAC_PTP_FV2(ptpfv2); + /* + * The default value (0xFFFC) allows the timestamping of both + * received Sync messages and Delay_Req messages. + */ + ptpfv3 = 0xFFFFFFFC; + bfin_write_EMAC_PTP_FV3(ptpfv3); + + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + /* Clear all five comparison mask bits (bits[12:8]) in the + * EMAC_PTP_CTL register to enable all the field matches. + */ + ptpctl &= ~0x1F00; + bfin_write_EMAC_PTP_CTL(ptpctl); + /* + * Keep the default values of the EMAC_PTP_FOFF register, except set + * the PTPCOF field to 0x2A. + */ + ptpfoff = 0x2A24170C; + bfin_write_EMAC_PTP_FOFF(ptpfoff); + /* + * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2 + * registers. + */ + ptpfv1 = 0x11040800; + bfin_write_EMAC_PTP_FV1(ptpfv1); + ptpfv2 = 0x0140013F; + bfin_write_EMAC_PTP_FV2(ptpfv2); + /* + * To allow the timestamping of Pdelay_Req and Pdelay_Resp, set + * the value to 0xFFF0. + */ + ptpfv3 = 0xFFFFFFF0; + bfin_write_EMAC_PTP_FV3(ptpfv3); + + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + /* + * Clear bits 8 and 12 of the EMAC_PTP_CTL register to enable only the + * EFTM and PTPCM field comparison. + */ + ptpctl &= ~0x1100; + bfin_write_EMAC_PTP_CTL(ptpctl); + /* + * Keep the default values of all the fields of the EMAC_PTP_FOFF + * register, except set the PTPCOF field to 0x0E. + */ + ptpfoff = 0x0E24170C; + bfin_write_EMAC_PTP_FOFF(ptpfoff); + /* + * Program bits [15:0] of the EMAC_PTP_FV1 register to 0x88F7, which + * corresponds to PTP messages on the MAC layer. + */ + ptpfv1 = 0x110488F7; + bfin_write_EMAC_PTP_FV1(ptpfv1); + ptpfv2 = 0x0140013F; + bfin_write_EMAC_PTP_FV2(ptpfv2); + /* + * To allow the timestamping of Pdelay_Req and Pdelay_Resp + * messages, set the value to 0xFFF0. + */ + ptpfv3 = 0xFFFFFFF0; + bfin_write_EMAC_PTP_FV3(ptpfv3); + + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + break; + default: + return -ERANGE; + } + + if (config.tx_type == HWTSTAMP_TX_OFF && + bfin_mac_hwtstamp_is_none(config.rx_filter)) { + ptpctl &= ~PTP_EN; + bfin_write_EMAC_PTP_CTL(ptpctl); + + SSYNC(); + } else { + ptpctl |= PTP_EN; + bfin_write_EMAC_PTP_CTL(ptpctl); + + /* + * clear any existing timestamp + */ + bfin_read_EMAC_PTP_RXSNAPLO(); + bfin_read_EMAC_PTP_RXSNAPHI(); + + bfin_read_EMAC_PTP_TXSNAPLO(); + bfin_read_EMAC_PTP_TXSNAPHI(); + + /* + * Set registers so that rollover occurs soon to test this. + */ + bfin_write_EMAC_PTP_TIMELO(0x00000000); + bfin_write_EMAC_PTP_TIMEHI(0xFF800000); + + SSYNC(); + + lp->compare.last_update = 0; + timecounter_init(&lp->clock, + &lp->cycles, + ktime_to_ns(ktime_get_real())); + timecompare_update(&lp->compare, 0); + } + + lp->stamp_cfg = config; + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static void bfin_dump_hwtamp(char *s, ktime_t *hw, ktime_t *ts, struct timecompare *cmp) +{ + ktime_t sys = ktime_get_real(); + + pr_debug("%s %s hardware:%d,%d transform system:%d,%d system:%d,%d, cmp:%lld, %lld\n", + __func__, s, hw->tv.sec, hw->tv.nsec, ts->tv.sec, ts->tv.nsec, sys.tv.sec, + sys.tv.nsec, cmp->offset, cmp->skew); +} + +static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) +{ + struct bfin_mac_local *lp = netdev_priv(netdev); + union skb_shared_tx *shtx = skb_tx(skb); + + if (shtx->hardware) { + int timeout_cnt = MAX_TIMEOUT_CNT; + + /* When doing time stamping, keep the connection to the socket + * a while longer + */ + shtx->in_progress = 1; + + /* + * The timestamping is done at the EMAC module's MII/RMII interface + * when the module sees the Start of Frame of an event message packet. This + * interface is the closest possible place to the physical Ethernet transmission + * medium, providing the best timing accuracy. + */ + while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt)) + udelay(1); + if (timeout_cnt == 0) + printk(KERN_ERR DRV_NAME + ": fails to timestamp the TX packet\n"); + else { + struct skb_shared_hwtstamps shhwtstamps; + u64 ns; + u64 regval; + + regval = bfin_read_EMAC_PTP_TXSNAPLO(); + regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32; + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + ns = timecounter_cyc2time(&lp->clock, + regval); + timecompare_update(&lp->compare, ns); + shhwtstamps.hwtstamp = ns_to_ktime(ns); + shhwtstamps.syststamp = + timecompare_transform(&lp->compare, ns); + skb_tstamp_tx(skb, &shhwtstamps); + + bfin_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare); + } + } +} + +static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) +{ + struct bfin_mac_local *lp = netdev_priv(netdev); + u32 valid; + u64 regval, ns; + struct skb_shared_hwtstamps *shhwtstamps; + + if (bfin_mac_hwtstamp_is_none(lp->stamp_cfg.rx_filter)) + return; + + valid = bfin_read_EMAC_PTP_ISTAT() & RXEL; + if (!valid) + return; + + shhwtstamps = skb_hwtstamps(skb); + + regval = bfin_read_EMAC_PTP_RXSNAPLO(); + regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32; + ns = timecounter_cyc2time(&lp->clock, regval); + timecompare_update(&lp->compare, ns); + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns); + + bfin_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare); +} + +/* + * bfin_read_clock - read raw cycle counter (to be used by time counter) + */ +static cycle_t bfin_read_clock(const struct cyclecounter *tc) +{ + u64 stamp; + + stamp = bfin_read_EMAC_PTP_TIMELO(); + stamp |= (u64)bfin_read_EMAC_PTP_TIMEHI() << 32ULL; + + return stamp; +} + +#define PTP_CLK 25000000 + +static void bfin_mac_hwtstamp_init(struct net_device *netdev) +{ + struct bfin_mac_local *lp = netdev_priv(netdev); + u64 append; + + /* Initialize hardware timer */ + append = PTP_CLK * (1ULL << 32); + do_div(append, get_sclk()); + bfin_write_EMAC_PTP_ADDEND((u32)append); + + memset(&lp->cycles, 0, sizeof(lp->cycles)); + lp->cycles.read = bfin_read_clock; + lp->cycles.mask = CLOCKSOURCE_MASK(64); + lp->cycles.mult = 1000000000 / PTP_CLK; + lp->cycles.shift = 0; + + /* Synchronize our NIC clock against system wall clock */ + memset(&lp->compare, 0, sizeof(lp->compare)); + lp->compare.source = &lp->clock; + lp->compare.target = ktime_get_real; + lp->compare.num_samples = 10; + + /* Initialize hwstamp config */ + lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE; + lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF; +} + +#else +# define bfin_mac_hwtstamp_is_none(cfg) 0 +# define bfin_mac_hwtstamp_init(dev) +# define bfin_mac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP) +# define bfin_rx_hwtstamp(dev, skb) +# define bfin_tx_hwtstamp(dev, skb) +#endif + static void adjust_tx_list(void) { int timeout_cnt = MAX_TIMEOUT_CNT; @@ -608,18 +979,32 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb, { u16 *data; u32 data_align = (unsigned long)(skb->data) & 0x3; + union skb_shared_tx *shtx = skb_tx(skb); + current_tx_ptr->skb = skb; if (data_align == 0x2) { /* move skb->data to current_tx_ptr payload */ data = (u16 *)(skb->data) - 1; - *data = (u16)(skb->len); + *data = (u16)(skb->len); + /* + * When transmitting an Ethernet packet, the PTP_TSYNC module requires + * a DMA_Length_Word field associated with the packet. The lower 12 bits + * of this field are the length of the packet payload in bytes and the higher + * 4 bits are the timestamping enable field. + */ + if (shtx->hardware) + *data |= 0x1000; + current_tx_ptr->desc_a.start_addr = (u32)data; /* this is important! */ blackfin_dcache_flush_range((u32)data, (u32)((u8 *)data + skb->len + 4)); } else { *((u16 *)(current_tx_ptr->packet)) = (u16)(skb->len); + /* enable timestamping for the sent packet */ + if (shtx->hardware) + *((u16 *)(current_tx_ptr->packet)) |= 0x1000; memcpy((u8 *)(current_tx_ptr->packet + 2), skb->data, skb->len); current_tx_ptr->desc_a.start_addr = @@ -653,20 +1038,42 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb, out: adjust_tx_list(); + + bfin_tx_hwtstamp(dev, skb); + current_tx_ptr = current_tx_ptr->next; - dev->trans_start = jiffies; dev->stats.tx_packets++; dev->stats.tx_bytes += (skb->len); return NETDEV_TX_OK; } +#define IP_HEADER_OFF 0 +#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \ + RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE) + static void bfin_mac_rx(struct net_device *dev) { struct sk_buff *skb, *new_skb; unsigned short len; + struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev); +#if defined(BFIN_MAC_CSUM_OFFLOAD) + unsigned int i; + unsigned char fcs[ETH_FCS_LEN + 1]; +#endif + + /* check if frame status word reports an error condition + * we which case we simply drop the packet + */ + if (current_rx_ptr->status.status_word & RX_ERROR_MASK) { + printk(KERN_NOTICE DRV_NAME + ": rx: receive error - packet dropped\n"); + dev->stats.rx_dropped++; + goto out; + } /* allocate a new skb for next time receive */ skb = current_rx_ptr->skb; + new_skb = dev_alloc_skb(PKT_BUF_SZ + NET_IP_ALIGN); if (!new_skb) { printk(KERN_NOTICE DRV_NAME @@ -676,34 +1083,59 @@ static void bfin_mac_rx(struct net_device *dev) } /* reserve 2 bytes for RXDWA padding */ skb_reserve(new_skb, NET_IP_ALIGN); - current_rx_ptr->skb = new_skb; - current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2; - /* Invidate the data cache of skb->data range when it is write back * cache. It will prevent overwritting the new data from DMA */ blackfin_dcache_invalidate_range((unsigned long)new_skb->head, (unsigned long)new_skb->end); + current_rx_ptr->skb = new_skb; + current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2; + len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN); + /* Deduce Ethernet FCS length from Ethernet payload length */ + len -= ETH_FCS_LEN; skb_put(skb, len); - blackfin_dcache_invalidate_range((unsigned long)skb->head, - (unsigned long)skb->tail); skb->protocol = eth_type_trans(skb, dev); + + bfin_rx_hwtstamp(dev, skb); + #if defined(BFIN_MAC_CSUM_OFFLOAD) - skb->csum = current_rx_ptr->status.ip_payload_csum; - skb->ip_summed = CHECKSUM_COMPLETE; + /* Checksum offloading only works for IPv4 packets with the standard IP header + * length of 20 bytes, because the blackfin MAC checksum calculation is + * based on that assumption. We must NOT use the calculated checksum if our + * IP version or header break that assumption. + */ + if (skb->data[IP_HEADER_OFF] == 0x45) { + skb->csum = current_rx_ptr->status.ip_payload_csum; + /* + * Deduce Ethernet FCS from hardware generated IP payload checksum. + * IP checksum is based on 16-bit one's complement algorithm. + * To deduce a value from checksum is equal to add its inversion. + * If the IP payload len is odd, the inversed FCS should also + * begin from odd address and leave first byte zero. + */ + if (skb->len % 2) { + fcs[0] = 0; + for (i = 0; i < ETH_FCS_LEN; i++) + fcs[i + 1] = ~skb->data[skb->len + i]; + skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum); + } else { + for (i = 0; i < ETH_FCS_LEN; i++) + fcs[i] = ~skb->data[skb->len + i]; + skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum); + } + skb->ip_summed = CHECKSUM_COMPLETE; + } #endif netif_rx(skb); dev->stats.rx_packets++; dev->stats.rx_bytes += len; +out: current_rx_ptr->status.status_word = 0x00000000; current_rx_ptr = current_rx_ptr->next; - -out: - return; } /* interrupt routine to handle rx and error signal */ @@ -755,8 +1187,9 @@ static void bfin_mac_disable(void) /* * Enable Interrupts, Receive, and Transmit */ -static void bfin_mac_enable(void) +static int bfin_mac_enable(void) { + int ret; u32 opmode; pr_debug("%s: %s\n", DRV_NAME, __func__); @@ -766,7 +1199,9 @@ static void bfin_mac_enable(void) bfin_write_DMA1_CONFIG(rx_list_head->desc_a.config); /* Wait MII done */ - bfin_mdio_poll(); + ret = bfin_mdio_poll(); + if (ret) + return ret; /* We enable only RX here */ /* ASTP : Enable Automatic Pad Stripping @@ -790,6 +1225,8 @@ static void bfin_mac_enable(void) #endif /* Turn on the EMAC rx */ bfin_write_EMAC_OPMODE(opmode); + + return 0; } /* Our watchdog timed out. Called by the networking layer */ @@ -805,21 +1242,21 @@ static void bfin_mac_timeout(struct net_device *dev) bfin_mac_enable(); /* We can accept TX packets again */ - dev->trans_start = jiffies; + dev->trans_start = jiffies; /* prevent tx timeout */ netif_wake_queue(dev); } static void bfin_mac_multicast_hash(struct net_device *dev) { u32 emac_hashhi, emac_hashlo; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; emac_hashhi = emac_hashlo = 0; - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* skip non-multicast addresses */ if (!(*addrs & 1)) @@ -836,8 +1273,6 @@ static void bfin_mac_multicast_hash(struct net_device *dev) bfin_write_EMAC_HASHHI(emac_hashhi); bfin_write_EMAC_HASHLO(emac_hashlo); - - return; } /* @@ -853,7 +1288,7 @@ static void bfin_mac_set_multicast_list(struct net_device *dev) if (dev->flags & IFF_PROMISC) { printk(KERN_INFO "%s: set to promisc mode\n", dev->name); sysctl = bfin_read_EMAC_OPMODE(); - sysctl |= RAF; + sysctl |= PR; bfin_write_EMAC_OPMODE(sysctl); } else if (dev->flags & IFF_ALLMULTI) { /* accept all multicast */ @@ -874,6 +1309,16 @@ static void bfin_mac_set_multicast_list(struct net_device *dev) } } +static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return bfin_mac_hwtstamp_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + /* * this puts the device in an inactive state */ @@ -894,7 +1339,7 @@ static void bfin_mac_shutdown(struct net_device *dev) static int bfin_mac_open(struct net_device *dev) { struct bfin_mac_local *lp = netdev_priv(dev); - int retval; + int ret; pr_debug("%s: %s\n", dev->name, __func__); /* @@ -908,18 +1353,21 @@ static int bfin_mac_open(struct net_device *dev) } /* initial rx and tx list */ - retval = desc_list_init(); - - if (retval) - return retval; + ret = desc_list_init(); + if (ret) + return ret; phy_start(lp->phydev); phy_write(lp->phydev, MII_BMCR, BMCR_RESET); setup_system_regs(dev); setup_mac_addr(dev->dev_addr); + bfin_mac_disable(); - bfin_mac_enable(); + ret = bfin_mac_enable(); + if (ret) + return ret; pr_debug("hardware init finished\n"); + netif_start_queue(dev); netif_carrier_on(dev); @@ -958,6 +1406,7 @@ static const struct net_device_ops bfin_mac_netdev_ops = { .ndo_set_mac_address = bfin_mac_set_mac_address, .ndo_tx_timeout = bfin_mac_timeout, .ndo_set_multicast_list = bfin_mac_set_multicast_list, + .ndo_do_ioctl = bfin_mac_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1017,6 +1466,11 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) } pd = pdev->dev.platform_data; lp->mii_bus = platform_get_drvdata(pd); + if (!lp->mii_bus) { + dev_err(&pdev->dev, "Cannot get mii_bus!\n"); + rc = -ENODEV; + goto out_err_mii_bus_probe; + } lp->mii_bus->priv = ndev; rc = mii_probe(ndev); @@ -1049,6 +1503,8 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) goto out_err_reg_ndev; } + bfin_mac_hwtstamp_init(ndev); + /* now, print out the card info, in a short format.. */ dev_info(&pdev->dev, "%s, Version %s\n", DRV_DESC, DRV_VERSION); @@ -1060,6 +1516,7 @@ out_err_request_irq: out_err_mii_probe: mdiobus_unregister(lp->mii_bus); mdiobus_free(lp->mii_bus); +out_err_mii_bus_probe: peripheral_free_list(pin_req); out_err_probe_mac: platform_set_drvdata(pdev, NULL); @@ -1092,9 +1549,16 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev) static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg) { struct net_device *net_dev = platform_get_drvdata(pdev); + struct bfin_mac_local *lp = netdev_priv(net_dev); - if (netif_running(net_dev)) - bfin_mac_close(net_dev); + if (lp->wol) { + bfin_write_EMAC_OPMODE((bfin_read_EMAC_OPMODE() & ~TE) | RE); + bfin_write_EMAC_WKUP_CTL(MPKE); + enable_irq_wake(IRQ_MAC_WAKEDET); + } else { + if (netif_running(net_dev)) + bfin_mac_close(net_dev); + } return 0; } @@ -1102,9 +1566,16 @@ static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg) static int bfin_mac_resume(struct platform_device *pdev) { struct net_device *net_dev = platform_get_drvdata(pdev); + struct bfin_mac_local *lp = netdev_priv(net_dev); - if (netif_running(net_dev)) - bfin_mac_open(net_dev); + if (lp->wol) { + bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE); + bfin_write_EMAC_WKUP_CTL(0); + disable_irq_wake(IRQ_MAC_WAKEDET); + } else { + if (netif_running(net_dev)) + bfin_mac_open(net_dev); + } return 0; } |