diff options
author | Richard Cochran <richardcochran@gmail.com> | 2012-10-31 02:27:24 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-01 11:41:35 -0400 |
commit | dd87b22f9096863adfd5daf281beb0a83379bd8f (patch) | |
tree | 6fe070016ab65b58245cb2910f2166f880dd8c12 /drivers/net/ethernet/adi | |
parent | bc3c5f634de23ca767c2d04d356ddf5bbe2144e2 (diff) |
bfin_mac: offer a PTP Hardware Clock.
The BF518 has a PTP time unit that works in a similar way to other MAC
based clocks, like gianfar, ixp46x, and igb. This patch adds support for
using the blackfin as a PHC. Although the blackfin hardware does offer a
few ancillary features, this patch implements only the basic operations.
Compile tested only.
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Tested-by: Bob Liu <lliubbo@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/adi')
-rw-r--r-- | drivers/net/ethernet/adi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/adi/bfin_mac.c | 170 | ||||
-rw-r--r-- | drivers/net/ethernet/adi/bfin_mac.h | 6 |
3 files changed, 175 insertions, 3 deletions
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index 49a30d37ae4a..175c38c077b2 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig | |||
@@ -61,7 +61,7 @@ config BFIN_RX_DESC_NUM | |||
61 | 61 | ||
62 | config BFIN_MAC_USE_HWSTAMP | 62 | config BFIN_MAC_USE_HWSTAMP |
63 | bool "Use IEEE 1588 hwstamp" | 63 | bool "Use IEEE 1588 hwstamp" |
64 | depends on BFIN_MAC && BF518 | 64 | depends on BFIN_MAC && BF518 && PTP_1588_CLOCK && !(BFIN_MAC=y && PTP_1588_CLOCK=m) |
65 | default y | 65 | default y |
66 | ---help--- | 66 | ---help--- |
67 | To support the IEEE 1588 Precision Time Protocol (PTP), select y here | 67 | To support the IEEE 1588 Precision Time Protocol (PTP), select y here |
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 885fa80e9220..f1c458dc039a 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c | |||
@@ -552,11 +552,13 @@ static int bfin_mac_ethtool_setwol(struct net_device *dev, | |||
552 | static int bfin_mac_ethtool_get_ts_info(struct net_device *dev, | 552 | static int bfin_mac_ethtool_get_ts_info(struct net_device *dev, |
553 | struct ethtool_ts_info *info) | 553 | struct ethtool_ts_info *info) |
554 | { | 554 | { |
555 | struct bfin_mac_local *lp = netdev_priv(dev); | ||
556 | |||
555 | info->so_timestamping = | 557 | info->so_timestamping = |
556 | SOF_TIMESTAMPING_TX_HARDWARE | | 558 | SOF_TIMESTAMPING_TX_HARDWARE | |
557 | SOF_TIMESTAMPING_RX_HARDWARE | | 559 | SOF_TIMESTAMPING_RX_HARDWARE | |
558 | SOF_TIMESTAMPING_RAW_HARDWARE; | 560 | SOF_TIMESTAMPING_RAW_HARDWARE; |
559 | info->phc_index = -1; | 561 | info->phc_index = lp->phc_index; |
560 | info->tx_types = | 562 | info->tx_types = |
561 | (1 << HWTSTAMP_TX_OFF) | | 563 | (1 << HWTSTAMP_TX_OFF) | |
562 | (1 << HWTSTAMP_TX_ON); | 564 | (1 << HWTSTAMP_TX_ON); |
@@ -887,7 +889,7 @@ static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) | |||
887 | static void bfin_mac_hwtstamp_init(struct net_device *netdev) | 889 | static void bfin_mac_hwtstamp_init(struct net_device *netdev) |
888 | { | 890 | { |
889 | struct bfin_mac_local *lp = netdev_priv(netdev); | 891 | struct bfin_mac_local *lp = netdev_priv(netdev); |
890 | u64 addend; | 892 | u64 addend, ppb; |
891 | u32 input_clk, phc_clk; | 893 | u32 input_clk, phc_clk; |
892 | 894 | ||
893 | /* Initialize hardware timer */ | 895 | /* Initialize hardware timer */ |
@@ -898,18 +900,175 @@ static void bfin_mac_hwtstamp_init(struct net_device *netdev) | |||
898 | bfin_write_EMAC_PTP_ADDEND((u32)addend); | 900 | bfin_write_EMAC_PTP_ADDEND((u32)addend); |
899 | 901 | ||
900 | lp->addend = addend; | 902 | lp->addend = addend; |
903 | ppb = 1000000000ULL * input_clk; | ||
904 | do_div(ppb, phc_clk); | ||
905 | lp->max_ppb = ppb - 1000000000ULL - 1ULL; | ||
901 | 906 | ||
902 | /* Initialize hwstamp config */ | 907 | /* Initialize hwstamp config */ |
903 | lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE; | 908 | lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE; |
904 | lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF; | 909 | lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF; |
905 | } | 910 | } |
906 | 911 | ||
912 | static u64 bfin_ptp_time_read(struct bfin_mac_local *lp) | ||
913 | { | ||
914 | u64 ns; | ||
915 | u32 lo, hi; | ||
916 | |||
917 | lo = bfin_read_EMAC_PTP_TIMELO(); | ||
918 | hi = bfin_read_EMAC_PTP_TIMEHI(); | ||
919 | |||
920 | ns = ((u64) hi) << 32; | ||
921 | ns |= lo; | ||
922 | ns <<= lp->shift; | ||
923 | |||
924 | return ns; | ||
925 | } | ||
926 | |||
927 | static void bfin_ptp_time_write(struct bfin_mac_local *lp, u64 ns) | ||
928 | { | ||
929 | u32 hi, lo; | ||
930 | |||
931 | ns >>= lp->shift; | ||
932 | hi = ns >> 32; | ||
933 | lo = ns & 0xffffffff; | ||
934 | |||
935 | bfin_write_EMAC_PTP_TIMELO(lo); | ||
936 | bfin_write_EMAC_PTP_TIMEHI(hi); | ||
937 | } | ||
938 | |||
939 | /* PTP Hardware Clock operations */ | ||
940 | |||
941 | static int bfin_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) | ||
942 | { | ||
943 | u64 adj; | ||
944 | u32 diff, addend; | ||
945 | int neg_adj = 0; | ||
946 | struct bfin_mac_local *lp = | ||
947 | container_of(ptp, struct bfin_mac_local, caps); | ||
948 | |||
949 | if (ppb < 0) { | ||
950 | neg_adj = 1; | ||
951 | ppb = -ppb; | ||
952 | } | ||
953 | addend = lp->addend; | ||
954 | adj = addend; | ||
955 | adj *= ppb; | ||
956 | diff = div_u64(adj, 1000000000ULL); | ||
957 | |||
958 | addend = neg_adj ? addend - diff : addend + diff; | ||
959 | |||
960 | bfin_write_EMAC_PTP_ADDEND(addend); | ||
961 | |||
962 | return 0; | ||
963 | } | ||
964 | |||
965 | static int bfin_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | ||
966 | { | ||
967 | s64 now; | ||
968 | unsigned long flags; | ||
969 | struct bfin_mac_local *lp = | ||
970 | container_of(ptp, struct bfin_mac_local, caps); | ||
971 | |||
972 | spin_lock_irqsave(&lp->phc_lock, flags); | ||
973 | |||
974 | now = bfin_ptp_time_read(lp); | ||
975 | now += delta; | ||
976 | bfin_ptp_time_write(lp, now); | ||
977 | |||
978 | spin_unlock_irqrestore(&lp->phc_lock, flags); | ||
979 | |||
980 | return 0; | ||
981 | } | ||
982 | |||
983 | static int bfin_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) | ||
984 | { | ||
985 | u64 ns; | ||
986 | u32 remainder; | ||
987 | unsigned long flags; | ||
988 | struct bfin_mac_local *lp = | ||
989 | container_of(ptp, struct bfin_mac_local, caps); | ||
990 | |||
991 | spin_lock_irqsave(&lp->phc_lock, flags); | ||
992 | |||
993 | ns = bfin_ptp_time_read(lp); | ||
994 | |||
995 | spin_unlock_irqrestore(&lp->phc_lock, flags); | ||
996 | |||
997 | ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); | ||
998 | ts->tv_nsec = remainder; | ||
999 | return 0; | ||
1000 | } | ||
1001 | |||
1002 | static int bfin_ptp_settime(struct ptp_clock_info *ptp, | ||
1003 | const struct timespec *ts) | ||
1004 | { | ||
1005 | u64 ns; | ||
1006 | unsigned long flags; | ||
1007 | struct bfin_mac_local *lp = | ||
1008 | container_of(ptp, struct bfin_mac_local, caps); | ||
1009 | |||
1010 | ns = ts->tv_sec * 1000000000ULL; | ||
1011 | ns += ts->tv_nsec; | ||
1012 | |||
1013 | spin_lock_irqsave(&lp->phc_lock, flags); | ||
1014 | |||
1015 | bfin_ptp_time_write(lp, ns); | ||
1016 | |||
1017 | spin_unlock_irqrestore(&lp->phc_lock, flags); | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | static int bfin_ptp_enable(struct ptp_clock_info *ptp, | ||
1023 | struct ptp_clock_request *rq, int on) | ||
1024 | { | ||
1025 | return -EOPNOTSUPP; | ||
1026 | } | ||
1027 | |||
1028 | static struct ptp_clock_info bfin_ptp_caps = { | ||
1029 | .owner = THIS_MODULE, | ||
1030 | .name = "BF518 clock", | ||
1031 | .max_adj = 0, | ||
1032 | .n_alarm = 0, | ||
1033 | .n_ext_ts = 0, | ||
1034 | .n_per_out = 0, | ||
1035 | .pps = 0, | ||
1036 | .adjfreq = bfin_ptp_adjfreq, | ||
1037 | .adjtime = bfin_ptp_adjtime, | ||
1038 | .gettime = bfin_ptp_gettime, | ||
1039 | .settime = bfin_ptp_settime, | ||
1040 | .enable = bfin_ptp_enable, | ||
1041 | }; | ||
1042 | |||
1043 | static int bfin_phc_init(struct net_device *netdev, struct device *dev) | ||
1044 | { | ||
1045 | struct bfin_mac_local *lp = netdev_priv(netdev); | ||
1046 | |||
1047 | lp->caps = bfin_ptp_caps; | ||
1048 | lp->caps.max_adj = lp->max_ppb; | ||
1049 | lp->clock = ptp_clock_register(&lp->caps, dev); | ||
1050 | if (IS_ERR(lp->clock)) | ||
1051 | return PTR_ERR(lp->clock); | ||
1052 | |||
1053 | lp->phc_index = ptp_clock_index(lp->clock); | ||
1054 | spin_lock_init(&lp->phc_lock); | ||
1055 | |||
1056 | return 0; | ||
1057 | } | ||
1058 | |||
1059 | static void bfin_phc_release(struct bfin_mac_local *lp) | ||
1060 | { | ||
1061 | ptp_clock_unregister(lp->clock); | ||
1062 | } | ||
1063 | |||
907 | #else | 1064 | #else |
908 | # define bfin_mac_hwtstamp_is_none(cfg) 0 | 1065 | # define bfin_mac_hwtstamp_is_none(cfg) 0 |
909 | # define bfin_mac_hwtstamp_init(dev) | 1066 | # define bfin_mac_hwtstamp_init(dev) |
910 | # define bfin_mac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP) | 1067 | # define bfin_mac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP) |
911 | # define bfin_rx_hwtstamp(dev, skb) | 1068 | # define bfin_rx_hwtstamp(dev, skb) |
912 | # define bfin_tx_hwtstamp(dev, skb) | 1069 | # define bfin_tx_hwtstamp(dev, skb) |
1070 | # define bfin_phc_init(netdev, dev) 0 | ||
1071 | # define bfin_phc_release(lp) | ||
913 | #endif | 1072 | #endif |
914 | 1073 | ||
915 | static inline void _tx_reclaim_skb(void) | 1074 | static inline void _tx_reclaim_skb(void) |
@@ -1544,12 +1703,17 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) | |||
1544 | } | 1703 | } |
1545 | 1704 | ||
1546 | bfin_mac_hwtstamp_init(ndev); | 1705 | bfin_mac_hwtstamp_init(ndev); |
1706 | if (bfin_phc_init(ndev, &pdev->dev)) { | ||
1707 | dev_err(&pdev->dev, "Cannot register PHC device!\n"); | ||
1708 | goto out_err_phc; | ||
1709 | } | ||
1547 | 1710 | ||
1548 | /* now, print out the card info, in a short format.. */ | 1711 | /* now, print out the card info, in a short format.. */ |
1549 | netdev_info(ndev, "%s, Version %s\n", DRV_DESC, DRV_VERSION); | 1712 | netdev_info(ndev, "%s, Version %s\n", DRV_DESC, DRV_VERSION); |
1550 | 1713 | ||
1551 | return 0; | 1714 | return 0; |
1552 | 1715 | ||
1716 | out_err_phc: | ||
1553 | out_err_reg_ndev: | 1717 | out_err_reg_ndev: |
1554 | free_irq(IRQ_MAC_RX, ndev); | 1718 | free_irq(IRQ_MAC_RX, ndev); |
1555 | out_err_request_irq: | 1719 | out_err_request_irq: |
@@ -1568,6 +1732,8 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev) | |||
1568 | struct net_device *ndev = platform_get_drvdata(pdev); | 1732 | struct net_device *ndev = platform_get_drvdata(pdev); |
1569 | struct bfin_mac_local *lp = netdev_priv(ndev); | 1733 | struct bfin_mac_local *lp = netdev_priv(ndev); |
1570 | 1734 | ||
1735 | bfin_phc_release(lp); | ||
1736 | |||
1571 | platform_set_drvdata(pdev, NULL); | 1737 | platform_set_drvdata(pdev, NULL); |
1572 | 1738 | ||
1573 | lp->mii_bus->priv = NULL; | 1739 | lp->mii_bus->priv = NULL; |
diff --git a/drivers/net/ethernet/adi/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h index 57f042c3111c..7a07ee07906b 100644 --- a/drivers/net/ethernet/adi/bfin_mac.h +++ b/drivers/net/ethernet/adi/bfin_mac.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define _BFIN_MAC_H_ | 11 | #define _BFIN_MAC_H_ |
12 | 12 | ||
13 | #include <linux/net_tstamp.h> | 13 | #include <linux/net_tstamp.h> |
14 | #include <linux/ptp_clock_kernel.h> | ||
14 | #include <linux/timer.h> | 15 | #include <linux/timer.h> |
15 | #include <linux/etherdevice.h> | 16 | #include <linux/etherdevice.h> |
16 | #include <linux/bfin_mac.h> | 17 | #include <linux/bfin_mac.h> |
@@ -94,7 +95,12 @@ struct bfin_mac_local { | |||
94 | #if defined(CONFIG_BFIN_MAC_USE_HWSTAMP) | 95 | #if defined(CONFIG_BFIN_MAC_USE_HWSTAMP) |
95 | u32 addend; | 96 | u32 addend; |
96 | unsigned int shift; | 97 | unsigned int shift; |
98 | s32 max_ppb; | ||
97 | struct hwtstamp_config stamp_cfg; | 99 | struct hwtstamp_config stamp_cfg; |
100 | struct ptp_clock_info caps; | ||
101 | struct ptp_clock *clock; | ||
102 | int phc_index; | ||
103 | spinlock_t phc_lock; /* protects time lo/hi registers */ | ||
98 | #endif | 104 | #endif |
99 | }; | 105 | }; |
100 | 106 | ||