diff options
author | Manfred Rudigier <Manfred.Rudigier@omicron.at> | 2010-04-08 19:10:03 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-13 04:41:31 -0400 |
commit | cc772ab7cdcaa24d1fae332d92a1602788644f7a (patch) | |
tree | efbcb9bf8c1c9214f612d2044321a30ada230255 /drivers/net/gianfar.c | |
parent | e44171f115de3dedf34064646206deb91549865f (diff) |
gianfar: Add hardware RX timestamping support
The device is configured to insert hardware timestamps into all
received packets. The RX timestamps are extracted from the padding
alingment bytes during the clean_rx_ring operation and copied into the
skb_shared_hwtstamps struct of the skb. This extraction only happens if
the rx_filter was set to something else than HWTSTAMP_FILTER_NONE with
the SIOCSHWTSTAMP ioctl command.
Hardware timestamping is only supported for eTSEC devices. To indicate
device support the new FSL_GIANFAR_DEV_HAS_TIMER flag was introduced.
Signed-off-by: Manfred Rudigier <manfred.rudigier@omicron.at>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r-- | drivers/net/gianfar.c | 66 |
1 files changed, 61 insertions, 5 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 5175233f11f2..d102484c4b36 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c | |||
@@ -82,6 +82,7 @@ | |||
82 | #include <linux/tcp.h> | 82 | #include <linux/tcp.h> |
83 | #include <linux/udp.h> | 83 | #include <linux/udp.h> |
84 | #include <linux/in.h> | 84 | #include <linux/in.h> |
85 | #include <linux/net_tstamp.h> | ||
85 | 86 | ||
86 | #include <asm/io.h> | 87 | #include <asm/io.h> |
87 | #include <asm/irq.h> | 88 | #include <asm/irq.h> |
@@ -377,6 +378,13 @@ static void gfar_init_mac(struct net_device *ndev) | |||
377 | rctrl |= RCTRL_PADDING(priv->padding); | 378 | rctrl |= RCTRL_PADDING(priv->padding); |
378 | } | 379 | } |
379 | 380 | ||
381 | /* Insert receive time stamps into padding alignment bytes */ | ||
382 | if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) { | ||
383 | rctrl &= ~RCTRL_PAL_MASK; | ||
384 | rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE | RCTRL_PADDING(8); | ||
385 | priv->padding = 8; | ||
386 | } | ||
387 | |||
380 | /* keep vlan related bits if it's enabled */ | 388 | /* keep vlan related bits if it's enabled */ |
381 | if (priv->vlgrp) { | 389 | if (priv->vlgrp) { |
382 | rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; | 390 | rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; |
@@ -501,7 +509,8 @@ void unlock_tx_qs(struct gfar_private *priv) | |||
501 | /* Returns 1 if incoming frames use an FCB */ | 509 | /* Returns 1 if incoming frames use an FCB */ |
502 | static inline int gfar_uses_fcb(struct gfar_private *priv) | 510 | static inline int gfar_uses_fcb(struct gfar_private *priv) |
503 | { | 511 | { |
504 | return priv->vlgrp || priv->rx_csum_enable; | 512 | return priv->vlgrp || priv->rx_csum_enable || |
513 | (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER); | ||
505 | } | 514 | } |
506 | 515 | ||
507 | static void free_tx_pointers(struct gfar_private *priv) | 516 | static void free_tx_pointers(struct gfar_private *priv) |
@@ -742,7 +751,8 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev) | |||
742 | FSL_GIANFAR_DEV_HAS_CSUM | | 751 | FSL_GIANFAR_DEV_HAS_CSUM | |
743 | FSL_GIANFAR_DEV_HAS_VLAN | | 752 | FSL_GIANFAR_DEV_HAS_VLAN | |
744 | FSL_GIANFAR_DEV_HAS_MAGIC_PACKET | | 753 | FSL_GIANFAR_DEV_HAS_MAGIC_PACKET | |
745 | FSL_GIANFAR_DEV_HAS_EXTENDED_HASH; | 754 | FSL_GIANFAR_DEV_HAS_EXTENDED_HASH | |
755 | FSL_GIANFAR_DEV_HAS_TIMER; | ||
746 | 756 | ||
747 | ctype = of_get_property(np, "phy-connection-type", NULL); | 757 | ctype = of_get_property(np, "phy-connection-type", NULL); |
748 | 758 | ||
@@ -772,6 +782,38 @@ err_grp_init: | |||
772 | return err; | 782 | return err; |
773 | } | 783 | } |
774 | 784 | ||
785 | static int gfar_hwtstamp_ioctl(struct net_device *netdev, | ||
786 | struct ifreq *ifr, int cmd) | ||
787 | { | ||
788 | struct hwtstamp_config config; | ||
789 | struct gfar_private *priv = netdev_priv(netdev); | ||
790 | |||
791 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) | ||
792 | return -EFAULT; | ||
793 | |||
794 | /* reserved for future extensions */ | ||
795 | if (config.flags) | ||
796 | return -EINVAL; | ||
797 | |||
798 | if (config.tx_type) | ||
799 | return -ERANGE; | ||
800 | |||
801 | switch (config.rx_filter) { | ||
802 | case HWTSTAMP_FILTER_NONE: | ||
803 | priv->hwts_rx_en = 0; | ||
804 | break; | ||
805 | default: | ||
806 | if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)) | ||
807 | return -ERANGE; | ||
808 | priv->hwts_rx_en = 1; | ||
809 | config.rx_filter = HWTSTAMP_FILTER_ALL; | ||
810 | break; | ||
811 | } | ||
812 | |||
813 | return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? | ||
814 | -EFAULT : 0; | ||
815 | } | ||
816 | |||
775 | /* Ioctl MII Interface */ | 817 | /* Ioctl MII Interface */ |
776 | static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | 818 | static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) |
777 | { | 819 | { |
@@ -780,6 +822,9 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | |||
780 | if (!netif_running(dev)) | 822 | if (!netif_running(dev)) |
781 | return -EINVAL; | 823 | return -EINVAL; |
782 | 824 | ||
825 | if (cmd == SIOCSHWTSTAMP) | ||
826 | return gfar_hwtstamp_ioctl(dev, rq, cmd); | ||
827 | |||
783 | if (!priv->phydev) | 828 | if (!priv->phydev) |
784 | return -ENODEV; | 829 | return -ENODEV; |
785 | 830 | ||
@@ -982,7 +1027,8 @@ static int gfar_probe(struct of_device *ofdev, | |||
982 | else | 1027 | else |
983 | priv->padding = 0; | 1028 | priv->padding = 0; |
984 | 1029 | ||
985 | if (dev->features & NETIF_F_IP_CSUM) | 1030 | if (dev->features & NETIF_F_IP_CSUM || |
1031 | priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) | ||
986 | dev->hard_header_len += GMAC_FCB_LEN; | 1032 | dev->hard_header_len += GMAC_FCB_LEN; |
987 | 1033 | ||
988 | /* Program the isrg regs only if number of grps > 1 */ | 1034 | /* Program the isrg regs only if number of grps > 1 */ |
@@ -2474,6 +2520,17 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, | |||
2474 | skb_pull(skb, amount_pull); | 2520 | skb_pull(skb, amount_pull); |
2475 | } | 2521 | } |
2476 | 2522 | ||
2523 | /* Get receive timestamp from the skb */ | ||
2524 | if (priv->hwts_rx_en) { | ||
2525 | struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); | ||
2526 | u64 *ns = (u64 *) skb->data; | ||
2527 | memset(shhwtstamps, 0, sizeof(*shhwtstamps)); | ||
2528 | shhwtstamps->hwtstamp = ns_to_ktime(*ns); | ||
2529 | } | ||
2530 | |||
2531 | if (priv->padding) | ||
2532 | skb_pull(skb, priv->padding); | ||
2533 | |||
2477 | if (priv->rx_csum_enable) | 2534 | if (priv->rx_csum_enable) |
2478 | gfar_rx_checksum(skb, fcb); | 2535 | gfar_rx_checksum(skb, fcb); |
2479 | 2536 | ||
@@ -2510,8 +2567,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) | |||
2510 | bdp = rx_queue->cur_rx; | 2567 | bdp = rx_queue->cur_rx; |
2511 | base = rx_queue->rx_bd_base; | 2568 | base = rx_queue->rx_bd_base; |
2512 | 2569 | ||
2513 | amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) + | 2570 | amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0); |
2514 | priv->padding; | ||
2515 | 2571 | ||
2516 | while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { | 2572 | while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { |
2517 | struct sk_buff *newskb; | 2573 | struct sk_buff *newskb; |