diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 32 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/common.h | 22 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 47 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 32 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 108 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 384 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h | 74 |
11 files changed, 732 insertions, 24 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index ae995a367c91..1aca0e6881bd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile | |||
@@ -4,4 +4,4 @@ stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o | |||
4 | stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ | 4 | stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ |
5 | chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ | 5 | chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ |
6 | dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ | 6 | dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ |
7 | mmc_core.o $(stmmac-y) | 7 | mmc_core.o stmmac_hwtstamp.o $(stmmac-y) |
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 63b6031e304a..37a3f93b487d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c | |||
@@ -122,8 +122,40 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, | |||
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) | ||
126 | { | ||
127 | struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; | ||
128 | |||
129 | if (priv->hwts_rx_en && !priv->extend_desc) | ||
130 | /* NOTE: Device will overwrite des3 with timestamp value if | ||
131 | * 1588-2002 time stamping is enabled, hence reinitialize it | ||
132 | * to keep explicit chaining in the descriptor. | ||
133 | */ | ||
134 | p->des3 = (unsigned int)(priv->dma_rx_phy + | ||
135 | (((priv->dirty_rx) + 1) % | ||
136 | priv->dma_rx_size) * | ||
137 | sizeof(struct dma_desc)); | ||
138 | } | ||
139 | |||
140 | static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) | ||
141 | { | ||
142 | struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; | ||
143 | |||
144 | if (priv->hw->desc->get_tx_ls(p) && !priv->extend_desc) | ||
145 | /* NOTE: Device will overwrite des3 with timestamp value if | ||
146 | * 1588-2002 time stamping is enabled, hence reinitialize it | ||
147 | * to keep explicit chaining in the descriptor. | ||
148 | */ | ||
149 | p->des3 = (unsigned int)(priv->dma_tx_phy + | ||
150 | (((priv->dirty_tx + 1) % | ||
151 | priv->dma_tx_size) * | ||
152 | sizeof(struct dma_desc))); | ||
153 | } | ||
154 | |||
125 | const struct stmmac_chain_mode_ops chain_mode_ops = { | 155 | const struct stmmac_chain_mode_ops chain_mode_ops = { |
126 | .init = stmmac_init_dma_chain, | 156 | .init = stmmac_init_dma_chain, |
127 | .is_jumbo_frm = stmmac_is_jumbo_frm, | 157 | .is_jumbo_frm = stmmac_is_jumbo_frm, |
128 | .jumbo_frm = stmmac_jumbo_frm, | 158 | .jumbo_frm = stmmac_jumbo_frm, |
159 | .refill_desc3 = stmmac_refill_desc3, | ||
160 | .clean_desc3 = stmmac_clean_desc3, | ||
129 | }; | 161 | }; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 942fdce3ab93..6fa975c55234 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h | |||
@@ -339,6 +339,14 @@ struct stmmac_desc_ops { | |||
339 | struct dma_desc *p); | 339 | struct dma_desc *p); |
340 | void (*rx_extended_status) (void *data, struct stmmac_extra_stats *x, | 340 | void (*rx_extended_status) (void *data, struct stmmac_extra_stats *x, |
341 | struct dma_extended_desc *p); | 341 | struct dma_extended_desc *p); |
342 | /* Set tx timestamp enable bit */ | ||
343 | void (*enable_tx_timestamp) (struct dma_desc *p); | ||
344 | /* get tx timestamp status */ | ||
345 | int (*get_tx_timestamp_status) (struct dma_desc *p); | ||
346 | /* get timestamp value */ | ||
347 | u64 (*get_timestamp) (void *desc, u32 ats); | ||
348 | /* get rx timestamp status */ | ||
349 | int (*get_rx_timestamp_status) (void *desc, u32 ats); | ||
342 | }; | 350 | }; |
343 | 351 | ||
344 | struct stmmac_dma_ops { | 352 | struct stmmac_dma_ops { |
@@ -398,6 +406,13 @@ struct stmmac_ops { | |||
398 | void (*get_adv) (void __iomem *ioaddr, struct rgmii_adv *adv); | 406 | void (*get_adv) (void __iomem *ioaddr, struct rgmii_adv *adv); |
399 | }; | 407 | }; |
400 | 408 | ||
409 | struct stmmac_hwtimestamp { | ||
410 | void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data); | ||
411 | void (*config_sub_second_increment) (void __iomem *ioaddr); | ||
412 | int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec); | ||
413 | int (*config_addend)(void __iomem *ioaddr, u32 addend); | ||
414 | }; | ||
415 | |||
401 | struct mac_link { | 416 | struct mac_link { |
402 | int port; | 417 | int port; |
403 | int duplex; | 418 | int duplex; |
@@ -412,9 +427,9 @@ struct mii_regs { | |||
412 | struct stmmac_ring_mode_ops { | 427 | struct stmmac_ring_mode_ops { |
413 | unsigned int (*is_jumbo_frm) (int len, int ehn_desc); | 428 | unsigned int (*is_jumbo_frm) (int len, int ehn_desc); |
414 | unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); | 429 | unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); |
415 | void (*refill_desc3) (int bfsize, struct dma_desc *p); | 430 | void (*refill_desc3) (void *priv, struct dma_desc *p); |
416 | void (*init_desc3) (struct dma_desc *p); | 431 | void (*init_desc3) (struct dma_desc *p); |
417 | void (*clean_desc3) (struct dma_desc *p); | 432 | void (*clean_desc3) (void *priv, struct dma_desc *p); |
418 | int (*set_16kib_bfsize) (int mtu); | 433 | int (*set_16kib_bfsize) (int mtu); |
419 | }; | 434 | }; |
420 | 435 | ||
@@ -423,6 +438,8 @@ struct stmmac_chain_mode_ops { | |||
423 | unsigned int extend_desc); | 438 | unsigned int extend_desc); |
424 | unsigned int (*is_jumbo_frm) (int len, int ehn_desc); | 439 | unsigned int (*is_jumbo_frm) (int len, int ehn_desc); |
425 | unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); | 440 | unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); |
441 | void (*refill_desc3) (void *priv, struct dma_desc *p); | ||
442 | void (*clean_desc3) (void *priv, struct dma_desc *p); | ||
426 | }; | 443 | }; |
427 | 444 | ||
428 | struct mac_device_info { | 445 | struct mac_device_info { |
@@ -431,6 +448,7 @@ struct mac_device_info { | |||
431 | const struct stmmac_dma_ops *dma; | 448 | const struct stmmac_dma_ops *dma; |
432 | const struct stmmac_ring_mode_ops *ring; | 449 | const struct stmmac_ring_mode_ops *ring; |
433 | const struct stmmac_chain_mode_ops *chain; | 450 | const struct stmmac_chain_mode_ops *chain; |
451 | const struct stmmac_hwtimestamp *ptp; | ||
434 | struct mii_regs mii; /* MII register Addresses */ | 452 | struct mii_regs mii; /* MII register Addresses */ |
435 | struct mac_link link; | 453 | struct mac_link link; |
436 | unsigned int synopsys_uid; | 454 | unsigned int synopsys_uid; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index c1b9ab23b3c5..0fbc8fafa706 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c | |||
@@ -378,6 +378,49 @@ static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) | |||
378 | return p->des01.erx.frame_length; | 378 | return p->des01.erx.frame_length; |
379 | } | 379 | } |
380 | 380 | ||
381 | static void enh_desc_enable_tx_timestamp(struct dma_desc *p) | ||
382 | { | ||
383 | p->des01.etx.time_stamp_enable = 1; | ||
384 | } | ||
385 | |||
386 | static int enh_desc_get_tx_timestamp_status(struct dma_desc *p) | ||
387 | { | ||
388 | return p->des01.etx.time_stamp_status; | ||
389 | } | ||
390 | |||
391 | static u64 enh_desc_get_timestamp(void *desc, u32 ats) | ||
392 | { | ||
393 | u64 ns; | ||
394 | |||
395 | if (ats) { | ||
396 | struct dma_extended_desc *p = (struct dma_extended_desc *)desc; | ||
397 | ns = p->des6; | ||
398 | /* convert high/sec time stamp value to nanosecond */ | ||
399 | ns += p->des7 * 1000000000ULL; | ||
400 | } else { | ||
401 | struct dma_desc *p = (struct dma_desc *)desc; | ||
402 | ns = p->des2; | ||
403 | ns += p->des3 * 1000000000ULL; | ||
404 | } | ||
405 | |||
406 | return ns; | ||
407 | } | ||
408 | |||
409 | static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats) | ||
410 | { | ||
411 | if (ats) { | ||
412 | struct dma_extended_desc *p = (struct dma_extended_desc *)desc; | ||
413 | return p->basic.des01.erx.ipc_csum_error; | ||
414 | } else { | ||
415 | struct dma_desc *p = (struct dma_desc *)desc; | ||
416 | if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) | ||
417 | /* timestamp is corrupted, hence don't store it */ | ||
418 | return 0; | ||
419 | else | ||
420 | return 1; | ||
421 | } | ||
422 | } | ||
423 | |||
381 | const struct stmmac_desc_ops enh_desc_ops = { | 424 | const struct stmmac_desc_ops enh_desc_ops = { |
382 | .tx_status = enh_desc_get_tx_status, | 425 | .tx_status = enh_desc_get_tx_status, |
383 | .rx_status = enh_desc_get_rx_status, | 426 | .rx_status = enh_desc_get_rx_status, |
@@ -395,4 +438,8 @@ const struct stmmac_desc_ops enh_desc_ops = { | |||
395 | .set_rx_owner = enh_desc_set_rx_owner, | 438 | .set_rx_owner = enh_desc_set_rx_owner, |
396 | .get_rx_frame_len = enh_desc_get_rx_frame_len, | 439 | .get_rx_frame_len = enh_desc_get_rx_frame_len, |
397 | .rx_extended_status = enh_desc_get_ext_status, | 440 | .rx_extended_status = enh_desc_get_ext_status, |
441 | .enable_tx_timestamp = enh_desc_enable_tx_timestamp, | ||
442 | .get_tx_timestamp_status = enh_desc_get_tx_timestamp_status, | ||
443 | .get_timestamp = enh_desc_get_timestamp, | ||
444 | .get_rx_timestamp_status = enh_desc_get_rx_timestamp_status, | ||
398 | }; | 445 | }; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 47d509435ebb..7cbcea348c3d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c | |||
@@ -219,6 +219,39 @@ static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) | |||
219 | return p->des01.rx.frame_length; | 219 | return p->des01.rx.frame_length; |
220 | } | 220 | } |
221 | 221 | ||
222 | static void ndesc_enable_tx_timestamp(struct dma_desc *p) | ||
223 | { | ||
224 | p->des01.tx.time_stamp_enable = 1; | ||
225 | } | ||
226 | |||
227 | static int ndesc_get_tx_timestamp_status(struct dma_desc *p) | ||
228 | { | ||
229 | return p->des01.tx.time_stamp_status; | ||
230 | } | ||
231 | |||
232 | static u64 ndesc_get_timestamp(void *desc, u32 ats) | ||
233 | { | ||
234 | struct dma_desc *p = (struct dma_desc *)desc; | ||
235 | u64 ns; | ||
236 | |||
237 | ns = p->des2; | ||
238 | /* convert high/sec time stamp value to nanosecond */ | ||
239 | ns += p->des3 * 1000000000ULL; | ||
240 | |||
241 | return ns; | ||
242 | } | ||
243 | |||
244 | static int ndesc_get_rx_timestamp_status(void *desc, u32 ats) | ||
245 | { | ||
246 | struct dma_desc *p = (struct dma_desc *)desc; | ||
247 | |||
248 | if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) | ||
249 | /* timestamp is corrupted, hence don't store it */ | ||
250 | return 0; | ||
251 | else | ||
252 | return 1; | ||
253 | } | ||
254 | |||
222 | const struct stmmac_desc_ops ndesc_ops = { | 255 | const struct stmmac_desc_ops ndesc_ops = { |
223 | .tx_status = ndesc_get_tx_status, | 256 | .tx_status = ndesc_get_tx_status, |
224 | .rx_status = ndesc_get_rx_status, | 257 | .rx_status = ndesc_get_rx_status, |
@@ -235,4 +268,8 @@ const struct stmmac_desc_ops ndesc_ops = { | |||
235 | .set_tx_owner = ndesc_set_tx_owner, | 268 | .set_tx_owner = ndesc_set_tx_owner, |
236 | .set_rx_owner = ndesc_set_rx_owner, | 269 | .set_rx_owner = ndesc_set_rx_owner, |
237 | .get_rx_frame_len = ndesc_get_rx_frame_len, | 270 | .get_rx_frame_len = ndesc_get_rx_frame_len, |
271 | .enable_tx_timestamp = ndesc_enable_tx_timestamp, | ||
272 | .get_tx_timestamp_status = ndesc_get_tx_timestamp_status, | ||
273 | .get_timestamp = ndesc_get_timestamp, | ||
274 | .get_rx_timestamp_status = ndesc_get_rx_timestamp_status, | ||
238 | }; | 275 | }; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 43fc699b21cc..d0265a7d5a54 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c | |||
@@ -87,11 +87,14 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc) | |||
87 | return ret; | 87 | return ret; |
88 | } | 88 | } |
89 | 89 | ||
90 | static void stmmac_refill_desc3(int bfsize, struct dma_desc *p) | 90 | static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) |
91 | { | 91 | { |
92 | /* Fill DES3 in case of RING mode */ | 92 | struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; |
93 | if (bfsize >= BUF_SIZE_8KiB) | 93 | |
94 | p->des3 = p->des2 + BUF_SIZE_8KiB; | 94 | if (unlikely(priv->plat->has_gmac)) |
95 | /* Fill DES3 in case of RING mode */ | ||
96 | if (priv->dma_buf_sz >= BUF_SIZE_8KiB) | ||
97 | p->des3 = p->des2 + BUF_SIZE_8KiB; | ||
95 | } | 98 | } |
96 | 99 | ||
97 | /* In ring mode we need to fill the desc3 because it is used as buffer */ | 100 | /* In ring mode we need to fill the desc3 because it is used as buffer */ |
@@ -100,7 +103,7 @@ static void stmmac_init_desc3(struct dma_desc *p) | |||
100 | p->des3 = p->des2 + BUF_SIZE_8KiB; | 103 | p->des3 = p->des2 + BUF_SIZE_8KiB; |
101 | } | 104 | } |
102 | 105 | ||
103 | static void stmmac_clean_desc3(struct dma_desc *p) | 106 | static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) |
104 | { | 107 | { |
105 | if (unlikely(p->des3)) | 108 | if (unlikely(p->des3)) |
106 | p->des3 = 0; | 109 | p->des3 = 0; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 5176cae44b03..a21d1b9c9094 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h | |||
@@ -99,6 +99,10 @@ struct stmmac_priv { | |||
99 | unsigned int mode; | 99 | unsigned int mode; |
100 | int extend_desc; | 100 | int extend_desc; |
101 | int pcs; | 101 | int pcs; |
102 | int hwts_tx_en; | ||
103 | int hwts_rx_en; | ||
104 | unsigned int default_addend; | ||
105 | u32 adv_ts; | ||
102 | }; | 106 | }; |
103 | 107 | ||
104 | extern int phyaddr; | 108 | extern int phyaddr; |
@@ -108,6 +112,7 @@ extern int stmmac_mdio_register(struct net_device *ndev); | |||
108 | extern void stmmac_set_ethtool_ops(struct net_device *netdev); | 112 | extern void stmmac_set_ethtool_ops(struct net_device *netdev); |
109 | extern const struct stmmac_desc_ops enh_desc_ops; | 113 | extern const struct stmmac_desc_ops enh_desc_ops; |
110 | extern const struct stmmac_desc_ops ndesc_ops; | 114 | extern const struct stmmac_desc_ops ndesc_ops; |
115 | extern const struct stmmac_hwtimestamp stmmac_ptp; | ||
111 | int stmmac_freeze(struct net_device *ndev); | 116 | int stmmac_freeze(struct net_device *ndev); |
112 | int stmmac_restore(struct net_device *ndev); | 117 | int stmmac_restore(struct net_device *ndev); |
113 | int stmmac_resume(struct net_device *ndev); | 118 | int stmmac_resume(struct net_device *ndev); |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 54e15db516fe..5b340c23fd6b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/interrupt.h> | 27 | #include <linux/interrupt.h> |
28 | #include <linux/mii.h> | 28 | #include <linux/mii.h> |
29 | #include <linux/phy.h> | 29 | #include <linux/phy.h> |
30 | #include <linux/net_tstamp.h> | ||
30 | #include <asm/io.h> | 31 | #include <asm/io.h> |
31 | 32 | ||
32 | #include "stmmac.h" | 33 | #include "stmmac.h" |
@@ -725,6 +726,35 @@ static int stmmac_set_coalesce(struct net_device *dev, | |||
725 | return 0; | 726 | return 0; |
726 | } | 727 | } |
727 | 728 | ||
729 | static int stmmac_get_ts_info(struct net_device *dev, | ||
730 | struct ethtool_ts_info *info) | ||
731 | { | ||
732 | struct stmmac_priv *priv = netdev_priv(dev); | ||
733 | |||
734 | if ((priv->hwts_tx_en) && (priv->hwts_rx_en)) { | ||
735 | |||
736 | info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | | ||
737 | SOF_TIMESTAMPING_RX_HARDWARE | | ||
738 | SOF_TIMESTAMPING_RAW_HARDWARE; | ||
739 | |||
740 | info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); | ||
741 | |||
742 | info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) | | ||
743 | (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | | ||
744 | (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | | ||
745 | (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | | ||
746 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | | ||
747 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | | ||
748 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | | ||
749 | (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | | ||
750 | (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | | ||
751 | (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | | ||
752 | (1 << HWTSTAMP_FILTER_ALL)); | ||
753 | return 0; | ||
754 | } else | ||
755 | return ethtool_op_get_ts_info(dev, info); | ||
756 | } | ||
757 | |||
728 | static const struct ethtool_ops stmmac_ethtool_ops = { | 758 | static const struct ethtool_ops stmmac_ethtool_ops = { |
729 | .begin = stmmac_check_if_running, | 759 | .begin = stmmac_check_if_running, |
730 | .get_drvinfo = stmmac_ethtool_getdrvinfo, | 760 | .get_drvinfo = stmmac_ethtool_getdrvinfo, |
@@ -744,7 +774,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = { | |||
744 | .get_eee = stmmac_ethtool_op_get_eee, | 774 | .get_eee = stmmac_ethtool_op_get_eee, |
745 | .set_eee = stmmac_ethtool_op_set_eee, | 775 | .set_eee = stmmac_ethtool_op_set_eee, |
746 | .get_sset_count = stmmac_get_sset_count, | 776 | .get_sset_count = stmmac_get_sset_count, |
747 | .get_ts_info = ethtool_op_get_ts_info, | 777 | .get_ts_info = stmmac_get_ts_info, |
748 | .get_coalesce = stmmac_get_coalesce, | 778 | .get_coalesce = stmmac_get_coalesce, |
749 | .set_coalesce = stmmac_set_coalesce, | 779 | .set_coalesce = stmmac_set_coalesce, |
750 | }; | 780 | }; |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c new file mode 100644 index 000000000000..380baeb016a9 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | Copyright (C) 2013 Vayavya Labs Pvt Ltd | ||
3 | |||
4 | This implements all the API for managing HW timestamp & PTP. | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify it | ||
7 | under the terms and conditions of the GNU General Public License, | ||
8 | version 2, as published by the Free Software Foundation. | ||
9 | |||
10 | This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License along with | ||
16 | this program; if not, write to the Free Software Foundation, Inc., | ||
17 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | |||
19 | The full GNU General Public License is included in this distribution in | ||
20 | the file called "COPYING". | ||
21 | |||
22 | Author: Rayagond Kokatanur <rayagond@vayavyalabs.com> | ||
23 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | ||
24 | *******************************************************************************/ | ||
25 | |||
26 | #include <linux/io.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include "common.h" | ||
29 | #include "stmmac_ptp.h" | ||
30 | |||
31 | static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data) | ||
32 | { | ||
33 | writel(data, ioaddr + PTP_TCR); | ||
34 | } | ||
35 | |||
36 | static void stmmac_config_sub_second_increment(void __iomem *ioaddr) | ||
37 | { | ||
38 | u32 value = readl(ioaddr + PTP_TCR); | ||
39 | unsigned long data; | ||
40 | |||
41 | /* Convert the ptp_clock to nano second | ||
42 | * formula = (1/ptp_clock) * 1000000000 | ||
43 | * where, ptp_clock = 50MHz. | ||
44 | */ | ||
45 | data = (1000000000ULL / 50000000); | ||
46 | |||
47 | /* 0.465ns accuracy */ | ||
48 | if (value & PTP_TCR_TSCTRLSSR) | ||
49 | data = (data * 100) / 465; | ||
50 | |||
51 | writel(data, ioaddr + PTP_SSIR); | ||
52 | } | ||
53 | |||
54 | static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) | ||
55 | { | ||
56 | int limit; | ||
57 | u32 value; | ||
58 | |||
59 | writel(sec, ioaddr + PTP_STSUR); | ||
60 | writel(nsec, ioaddr + PTP_STNSUR); | ||
61 | /* issue command to initialize the system time value */ | ||
62 | value = readl(ioaddr + PTP_TCR); | ||
63 | value |= PTP_TCR_TSINIT; | ||
64 | writel(value, ioaddr + PTP_TCR); | ||
65 | |||
66 | /* wait for present system time initialize to complete */ | ||
67 | limit = 10; | ||
68 | while (limit--) { | ||
69 | if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT)) | ||
70 | break; | ||
71 | mdelay(10); | ||
72 | } | ||
73 | if (limit < 0) | ||
74 | return -EBUSY; | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int stmmac_config_addend(void __iomem *ioaddr, u32 addend) | ||
80 | { | ||
81 | u32 value; | ||
82 | int limit; | ||
83 | |||
84 | writel(addend, ioaddr + PTP_TAR); | ||
85 | /* issue command to update the addend value */ | ||
86 | value = readl(ioaddr + PTP_TCR); | ||
87 | value |= PTP_TCR_TSADDREG; | ||
88 | writel(value, ioaddr + PTP_TCR); | ||
89 | |||
90 | /* wait for present addend update to complete */ | ||
91 | limit = 10; | ||
92 | while (limit--) { | ||
93 | if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG)) | ||
94 | break; | ||
95 | mdelay(10); | ||
96 | } | ||
97 | if (limit < 0) | ||
98 | return -EBUSY; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | const struct stmmac_hwtimestamp stmmac_ptp = { | ||
104 | .config_hw_tstamping = stmmac_config_hw_tstamping, | ||
105 | .init_systime = stmmac_init_systime, | ||
106 | .config_sub_second_increment = stmmac_config_sub_second_increment, | ||
107 | .config_addend = stmmac_config_addend, | ||
108 | }; | ||
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 180eed7168c1..6906772069e3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | |||
@@ -47,6 +47,8 @@ | |||
47 | #include <linux/debugfs.h> | 47 | #include <linux/debugfs.h> |
48 | #include <linux/seq_file.h> | 48 | #include <linux/seq_file.h> |
49 | #endif | 49 | #endif |
50 | #include <linux/net_tstamp.h> | ||
51 | #include "stmmac_ptp.h" | ||
50 | #include "stmmac.h" | 52 | #include "stmmac.h" |
51 | 53 | ||
52 | #undef STMMAC_DEBUG | 54 | #undef STMMAC_DEBUG |
@@ -311,6 +313,327 @@ static void stmmac_eee_adjust(struct stmmac_priv *priv) | |||
311 | priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link); | 313 | priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link); |
312 | } | 314 | } |
313 | 315 | ||
316 | /* stmmac_get_tx_hwtstamp: | ||
317 | * @priv : pointer to private device structure. | ||
318 | * @entry : descriptor index to be used. | ||
319 | * @skb : the socket buffer | ||
320 | * Description : | ||
321 | * This function will read timestamp from the descriptor & pass it to stack. | ||
322 | * and also perform some sanity checks. | ||
323 | */ | ||
324 | static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, | ||
325 | unsigned int entry, | ||
326 | struct sk_buff *skb) | ||
327 | { | ||
328 | struct skb_shared_hwtstamps shhwtstamp; | ||
329 | u64 ns; | ||
330 | void *desc = NULL; | ||
331 | |||
332 | if (!priv->hwts_tx_en) | ||
333 | return; | ||
334 | |||
335 | /* if skb doesn't support hw tstamp */ | ||
336 | if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))) | ||
337 | return; | ||
338 | |||
339 | if (priv->adv_ts) | ||
340 | desc = (priv->dma_etx + entry); | ||
341 | else | ||
342 | desc = (priv->dma_tx + entry); | ||
343 | |||
344 | /* check tx tstamp status */ | ||
345 | if (!priv->hw->desc->get_tx_timestamp_status((struct dma_desc *)desc)) | ||
346 | return; | ||
347 | |||
348 | /* get the valid tstamp */ | ||
349 | ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts); | ||
350 | |||
351 | memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); | ||
352 | shhwtstamp.hwtstamp = ns_to_ktime(ns); | ||
353 | /* pass tstamp to stack */ | ||
354 | skb_tstamp_tx(skb, &shhwtstamp); | ||
355 | |||
356 | return; | ||
357 | } | ||
358 | |||
359 | /* stmmac_get_rx_hwtstamp: | ||
360 | * @priv : pointer to private device structure. | ||
361 | * @entry : descriptor index to be used. | ||
362 | * @skb : the socket buffer | ||
363 | * Description : | ||
364 | * This function will read received packet's timestamp from the descriptor | ||
365 | * and pass it to stack. It also perform some sanity checks. | ||
366 | */ | ||
367 | static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, | ||
368 | unsigned int entry, | ||
369 | struct sk_buff *skb) | ||
370 | { | ||
371 | struct skb_shared_hwtstamps *shhwtstamp = NULL; | ||
372 | u64 ns; | ||
373 | void *desc = NULL; | ||
374 | |||
375 | if (!priv->hwts_rx_en) | ||
376 | return; | ||
377 | |||
378 | if (priv->adv_ts) | ||
379 | desc = (priv->dma_erx + entry); | ||
380 | else | ||
381 | desc = (priv->dma_rx + entry); | ||
382 | |||
383 | /* if rx tstamp is not valid */ | ||
384 | if (!priv->hw->desc->get_rx_timestamp_status(desc, priv->adv_ts)) | ||
385 | return; | ||
386 | |||
387 | /* get valid tstamp */ | ||
388 | ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts); | ||
389 | shhwtstamp = skb_hwtstamps(skb); | ||
390 | memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); | ||
391 | shhwtstamp->hwtstamp = ns_to_ktime(ns); | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * stmmac_hwtstamp_ioctl - control hardware timestamping. | ||
396 | * @dev: device pointer. | ||
397 | * @ifr: An IOCTL specefic structure, that can contain a pointer to | ||
398 | * a proprietary structure used to pass information to the driver. | ||
399 | * Description: | ||
400 | * This function configures the MAC to enable/disable both outgoing(TX) | ||
401 | * and incoming(RX) packets time stamping based on user input. | ||
402 | * Return Value: | ||
403 | * 0 on success and an appropriate -ve integer on failure. | ||
404 | */ | ||
405 | static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) | ||
406 | { | ||
407 | struct stmmac_priv *priv = netdev_priv(dev); | ||
408 | struct hwtstamp_config config; | ||
409 | struct timespec now; | ||
410 | u64 temp = 0; | ||
411 | u32 ptp_v2 = 0; | ||
412 | u32 tstamp_all = 0; | ||
413 | u32 ptp_over_ipv4_udp = 0; | ||
414 | u32 ptp_over_ipv6_udp = 0; | ||
415 | u32 ptp_over_ethernet = 0; | ||
416 | u32 snap_type_sel = 0; | ||
417 | u32 ts_master_en = 0; | ||
418 | u32 ts_event_en = 0; | ||
419 | u32 value = 0; | ||
420 | |||
421 | if (!(priv->dma_cap.time_stamp || priv->adv_ts)) { | ||
422 | netdev_alert(priv->dev, "No support for HW time stamping\n"); | ||
423 | priv->hwts_tx_en = 0; | ||
424 | priv->hwts_rx_en = 0; | ||
425 | |||
426 | return -EOPNOTSUPP; | ||
427 | } | ||
428 | |||
429 | if (copy_from_user(&config, ifr->ifr_data, | ||
430 | sizeof(struct hwtstamp_config))) | ||
431 | return -EFAULT; | ||
432 | |||
433 | pr_debug("%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n", | ||
434 | __func__, config.flags, config.tx_type, config.rx_filter); | ||
435 | |||
436 | /* reserved for future extensions */ | ||
437 | if (config.flags) | ||
438 | return -EINVAL; | ||
439 | |||
440 | switch (config.tx_type) { | ||
441 | case HWTSTAMP_TX_OFF: | ||
442 | priv->hwts_tx_en = 0; | ||
443 | break; | ||
444 | case HWTSTAMP_TX_ON: | ||
445 | priv->hwts_tx_en = 1; | ||
446 | break; | ||
447 | default: | ||
448 | return -ERANGE; | ||
449 | } | ||
450 | |||
451 | if (priv->adv_ts) { | ||
452 | switch (config.rx_filter) { | ||
453 | /* time stamp no incoming packet at all */ | ||
454 | case HWTSTAMP_FILTER_NONE: | ||
455 | config.rx_filter = HWTSTAMP_FILTER_NONE; | ||
456 | break; | ||
457 | |||
458 | /* PTP v1, UDP, any kind of event packet */ | ||
459 | case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | ||
460 | config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; | ||
461 | /* take time stamp for all event messages */ | ||
462 | snap_type_sel = PTP_TCR_SNAPTYPSEL_1; | ||
463 | |||
464 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
465 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
466 | break; | ||
467 | |||
468 | /* PTP v1, UDP, Sync packet */ | ||
469 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | ||
470 | config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC; | ||
471 | /* take time stamp for SYNC messages only */ | ||
472 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
473 | |||
474 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
475 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
476 | break; | ||
477 | |||
478 | /* PTP v1, UDP, Delay_req packet */ | ||
479 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | ||
480 | config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ; | ||
481 | /* take time stamp for Delay_Req messages only */ | ||
482 | ts_master_en = PTP_TCR_TSMSTRENA; | ||
483 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
484 | |||
485 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
486 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
487 | break; | ||
488 | |||
489 | /* PTP v2, UDP, any kind of event packet */ | ||
490 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | ||
491 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; | ||
492 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
493 | /* take time stamp for all event messages */ | ||
494 | snap_type_sel = PTP_TCR_SNAPTYPSEL_1; | ||
495 | |||
496 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
497 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
498 | break; | ||
499 | |||
500 | /* PTP v2, UDP, Sync packet */ | ||
501 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | ||
502 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC; | ||
503 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
504 | /* take time stamp for SYNC messages only */ | ||
505 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
506 | |||
507 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
508 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
509 | break; | ||
510 | |||
511 | /* PTP v2, UDP, Delay_req packet */ | ||
512 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | ||
513 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ; | ||
514 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
515 | /* take time stamp for Delay_Req messages only */ | ||
516 | ts_master_en = PTP_TCR_TSMSTRENA; | ||
517 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
518 | |||
519 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
520 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
521 | break; | ||
522 | |||
523 | /* PTP v2/802.AS1, any layer, any kind of event packet */ | ||
524 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
525 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; | ||
526 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
527 | /* take time stamp for all event messages */ | ||
528 | snap_type_sel = PTP_TCR_SNAPTYPSEL_1; | ||
529 | |||
530 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
531 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
532 | ptp_over_ethernet = PTP_TCR_TSIPENA; | ||
533 | break; | ||
534 | |||
535 | /* PTP v2/802.AS1, any layer, Sync packet */ | ||
536 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | ||
537 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; | ||
538 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
539 | /* take time stamp for SYNC messages only */ | ||
540 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
541 | |||
542 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
543 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
544 | ptp_over_ethernet = PTP_TCR_TSIPENA; | ||
545 | break; | ||
546 | |||
547 | /* PTP v2/802.AS1, any layer, Delay_req packet */ | ||
548 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | ||
549 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; | ||
550 | ptp_v2 = PTP_TCR_TSVER2ENA; | ||
551 | /* take time stamp for Delay_Req messages only */ | ||
552 | ts_master_en = PTP_TCR_TSMSTRENA; | ||
553 | ts_event_en = PTP_TCR_TSEVNTENA; | ||
554 | |||
555 | ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; | ||
556 | ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; | ||
557 | ptp_over_ethernet = PTP_TCR_TSIPENA; | ||
558 | break; | ||
559 | |||
560 | /* time stamp any incoming packet */ | ||
561 | case HWTSTAMP_FILTER_ALL: | ||
562 | config.rx_filter = HWTSTAMP_FILTER_ALL; | ||
563 | tstamp_all = PTP_TCR_TSENALL; | ||
564 | break; | ||
565 | |||
566 | default: | ||
567 | return -ERANGE; | ||
568 | } | ||
569 | } else { | ||
570 | switch (config.rx_filter) { | ||
571 | case HWTSTAMP_FILTER_NONE: | ||
572 | config.rx_filter = HWTSTAMP_FILTER_NONE; | ||
573 | break; | ||
574 | default: | ||
575 | /* PTP v1, UDP, any kind of event packet */ | ||
576 | config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1); | ||
581 | |||
582 | if (!priv->hwts_tx_en && !priv->hwts_rx_en) | ||
583 | priv->hw->ptp->config_hw_tstamping(priv->ioaddr, 0); | ||
584 | else { | ||
585 | value = (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR | | ||
586 | tstamp_all | ptp_v2 | ptp_over_ethernet | | ||
587 | ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en | | ||
588 | ts_master_en | snap_type_sel); | ||
589 | |||
590 | priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value); | ||
591 | |||
592 | /* program Sub Second Increment reg */ | ||
593 | priv->hw->ptp->config_sub_second_increment(priv->ioaddr); | ||
594 | |||
595 | /* calculate default added value: | ||
596 | * formula is : | ||
597 | * addend = (2^32)/freq_div_ratio; | ||
598 | * where, freq_div_ratio = STMMAC_SYSCLOCK/50MHz | ||
599 | * hence, addend = ((2^32) * 50MHz)/STMMAC_SYSCLOCK; | ||
600 | * NOTE: STMMAC_SYSCLOCK should be >= 50MHz to | ||
601 | * achive 20ns accuracy. | ||
602 | * | ||
603 | * 2^x * y == (y << x), hence | ||
604 | * 2^32 * 50000000 ==> (50000000 << 32) | ||
605 | */ | ||
606 | temp = (u64)(50000000ULL << 32); | ||
607 | priv->default_addend = div_u64(temp, STMMAC_SYSCLOCK); | ||
608 | priv->hw->ptp->config_addend(priv->ioaddr, | ||
609 | priv->default_addend); | ||
610 | |||
611 | /* initialize system time */ | ||
612 | getnstimeofday(&now); | ||
613 | priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec, | ||
614 | now.tv_nsec); | ||
615 | } | ||
616 | |||
617 | return copy_to_user(ifr->ifr_data, &config, | ||
618 | sizeof(struct hwtstamp_config)) ? -EFAULT : 0; | ||
619 | } | ||
620 | |||
621 | static void stmmac_init_ptp(struct stmmac_priv *priv) | ||
622 | { | ||
623 | if (priv->dma_cap.time_stamp) { | ||
624 | pr_debug("IEEE 1588-2002 Time Stamp supported\n"); | ||
625 | priv->adv_ts = 0; | ||
626 | } | ||
627 | if (priv->dma_cap.atime_stamp && priv->extend_desc) { | ||
628 | pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n"); | ||
629 | priv->adv_ts = 1; | ||
630 | } | ||
631 | |||
632 | priv->hw->ptp = &stmmac_ptp; | ||
633 | priv->hwts_tx_en = 0; | ||
634 | priv->hwts_rx_en = 0; | ||
635 | } | ||
636 | |||
314 | /** | 637 | /** |
315 | * stmmac_adjust_link | 638 | * stmmac_adjust_link |
316 | * @dev: net device structure | 639 | * @dev: net device structure |
@@ -856,6 +1179,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) | |||
856 | priv->xstats.tx_pkt_n++; | 1179 | priv->xstats.tx_pkt_n++; |
857 | } else | 1180 | } else |
858 | priv->dev->stats.tx_errors++; | 1181 | priv->dev->stats.tx_errors++; |
1182 | |||
1183 | stmmac_get_tx_hwtstamp(priv, entry, skb); | ||
859 | } | 1184 | } |
860 | TX_DBG("%s: curr %d, dirty %d\n", __func__, | 1185 | TX_DBG("%s: curr %d, dirty %d\n", __func__, |
861 | priv->cur_tx, priv->dirty_tx); | 1186 | priv->cur_tx, priv->dirty_tx); |
@@ -867,8 +1192,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) | |||
867 | DMA_TO_DEVICE); | 1192 | DMA_TO_DEVICE); |
868 | priv->tx_skbuff_dma[entry] = 0; | 1193 | priv->tx_skbuff_dma[entry] = 0; |
869 | } | 1194 | } |
870 | if (priv->mode == STMMAC_RING_MODE) | 1195 | priv->hw->ring->clean_desc3(priv, p); |
871 | priv->hw->ring->clean_desc3(p); | ||
872 | 1196 | ||
873 | if (likely(skb != NULL)) { | 1197 | if (likely(skb != NULL)) { |
874 | dev_kfree_skb(skb); | 1198 | dev_kfree_skb(skb); |
@@ -1243,6 +1567,8 @@ static int stmmac_open(struct net_device *dev) | |||
1243 | 1567 | ||
1244 | stmmac_mmc_setup(priv); | 1568 | stmmac_mmc_setup(priv); |
1245 | 1569 | ||
1570 | stmmac_init_ptp(priv); | ||
1571 | |||
1246 | #ifdef CONFIG_STMMAC_DEBUG_FS | 1572 | #ifdef CONFIG_STMMAC_DEBUG_FS |
1247 | ret = stmmac_init_fs(dev); | 1573 | ret = stmmac_init_fs(dev); |
1248 | if (ret < 0) | 1574 | if (ret < 0) |
@@ -1507,7 +1833,15 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1507 | 1833 | ||
1508 | dev->stats.tx_bytes += skb->len; | 1834 | dev->stats.tx_bytes += skb->len; |
1509 | 1835 | ||
1510 | skb_tx_timestamp(skb); | 1836 | if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && |
1837 | priv->hwts_tx_en)) { | ||
1838 | /* declare that device is doing timestamping */ | ||
1839 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; | ||
1840 | priv->hw->desc->enable_tx_timestamp(first); | ||
1841 | } | ||
1842 | |||
1843 | if (!priv->hwts_tx_en) | ||
1844 | skb_tx_timestamp(skb); | ||
1511 | 1845 | ||
1512 | priv->hw->dma->enable_dma_transmission(priv->ioaddr); | 1846 | priv->hw->dma->enable_dma_transmission(priv->ioaddr); |
1513 | 1847 | ||
@@ -1545,9 +1879,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) | |||
1545 | 1879 | ||
1546 | p->des2 = priv->rx_skbuff_dma[entry]; | 1880 | p->des2 = priv->rx_skbuff_dma[entry]; |
1547 | 1881 | ||
1548 | if (unlikely((priv->mode == STMMAC_RING_MODE) && | 1882 | priv->hw->ring->refill_desc3(priv, p); |
1549 | (priv->plat->has_gmac))) | ||
1550 | priv->hw->ring->refill_desc3(bfsize, p); | ||
1551 | 1883 | ||
1552 | RX_DBG(KERN_INFO "\trefill entry #%d\n", entry); | 1884 | RX_DBG(KERN_INFO "\trefill entry #%d\n", entry); |
1553 | } | 1885 | } |
@@ -1604,9 +1936,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) | |||
1604 | &priv->xstats, | 1936 | &priv->xstats, |
1605 | priv->dma_erx + | 1937 | priv->dma_erx + |
1606 | entry); | 1938 | entry); |
1607 | if (unlikely(status == discard_frame)) | 1939 | if (unlikely(status == discard_frame)) { |
1608 | priv->dev->stats.rx_errors++; | 1940 | priv->dev->stats.rx_errors++; |
1609 | else { | 1941 | if (priv->hwts_rx_en && !priv->extend_desc) { |
1942 | /* DESC2 & DESC3 will be overwitten by device | ||
1943 | * with timestamp value, hence reinitialize | ||
1944 | * them in stmmac_rx_refill() function so that | ||
1945 | * device can reuse it. | ||
1946 | */ | ||
1947 | priv->rx_skbuff[entry] = NULL; | ||
1948 | dma_unmap_single(priv->device, | ||
1949 | priv->rx_skbuff_dma[entry], | ||
1950 | priv->dma_buf_sz, DMA_FROM_DEVICE); | ||
1951 | } | ||
1952 | } else { | ||
1610 | struct sk_buff *skb; | 1953 | struct sk_buff *skb; |
1611 | int frame_len; | 1954 | int frame_len; |
1612 | 1955 | ||
@@ -1635,6 +1978,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) | |||
1635 | prefetch(skb->data - NET_IP_ALIGN); | 1978 | prefetch(skb->data - NET_IP_ALIGN); |
1636 | priv->rx_skbuff[entry] = NULL; | 1979 | priv->rx_skbuff[entry] = NULL; |
1637 | 1980 | ||
1981 | stmmac_get_rx_hwtstamp(priv, entry, skb); | ||
1982 | |||
1638 | skb_put(skb, frame_len); | 1983 | skb_put(skb, frame_len); |
1639 | dma_unmap_single(priv->device, | 1984 | dma_unmap_single(priv->device, |
1640 | priv->rx_skbuff_dma[entry], | 1985 | priv->rx_skbuff_dma[entry], |
@@ -1855,21 +2200,30 @@ static void stmmac_poll_controller(struct net_device *dev) | |||
1855 | * a proprietary structure used to pass information to the driver. | 2200 | * a proprietary structure used to pass information to the driver. |
1856 | * @cmd: IOCTL command | 2201 | * @cmd: IOCTL command |
1857 | * Description: | 2202 | * Description: |
1858 | * Currently there are no special functionality supported in IOCTL, just the | 2203 | * Currently it supports just the phy_mii_ioctl(...) and HW time stamping. |
1859 | * phy_mii_ioctl(...) can be invoked. | ||
1860 | */ | 2204 | */ |
1861 | static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | 2205 | static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) |
1862 | { | 2206 | { |
1863 | struct stmmac_priv *priv = netdev_priv(dev); | 2207 | struct stmmac_priv *priv = netdev_priv(dev); |
1864 | int ret; | 2208 | int ret = -EOPNOTSUPP; |
1865 | 2209 | ||
1866 | if (!netif_running(dev)) | 2210 | if (!netif_running(dev)) |
1867 | return -EINVAL; | 2211 | return -EINVAL; |
1868 | 2212 | ||
1869 | if (!priv->phydev) | 2213 | switch (cmd) { |
1870 | return -EINVAL; | 2214 | case SIOCGMIIPHY: |
1871 | 2215 | case SIOCGMIIREG: | |
1872 | ret = phy_mii_ioctl(priv->phydev, rq, cmd); | 2216 | case SIOCSMIIREG: |
2217 | if (!priv->phydev) | ||
2218 | return -EINVAL; | ||
2219 | ret = phy_mii_ioctl(priv->phydev, rq, cmd); | ||
2220 | break; | ||
2221 | case SIOCSHWTSTAMP: | ||
2222 | ret = stmmac_hwtstamp_ioctl(dev, rq); | ||
2223 | break; | ||
2224 | default: | ||
2225 | break; | ||
2226 | } | ||
1873 | 2227 | ||
1874 | return ret; | 2228 | return ret; |
1875 | } | 2229 | } |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h new file mode 100644 index 000000000000..3dbc047622fa --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h | |||
@@ -0,0 +1,74 @@ | |||
1 | /****************************************************************************** | ||
2 | PTP Header file | ||
3 | |||
4 | Copyright (C) 2013 Vayavya Labs Pvt Ltd | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify it | ||
7 | under the terms and conditions of the GNU General Public License, | ||
8 | version 2, as published by the Free Software Foundation. | ||
9 | |||
10 | This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License along with | ||
16 | this program; if not, write to the Free Software Foundation, Inc., | ||
17 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | |||
19 | The full GNU General Public License is included in this distribution in | ||
20 | the file called "COPYING". | ||
21 | |||
22 | Author: Rayagond Kokatanur <rayagond@vayavyalabs.com> | ||
23 | ******************************************************************************/ | ||
24 | |||
25 | #ifndef __STMMAC_PTP_H__ | ||
26 | #define __STMMAC_PTP_H__ | ||
27 | |||
28 | #define STMMAC_SYSCLOCK 62500000 | ||
29 | |||
30 | /* IEEE 1588 PTP register offsets */ | ||
31 | #define PTP_TCR 0x0700 /* Timestamp Control Reg */ | ||
32 | #define PTP_SSIR 0x0704 /* Sub-Second Increment Reg */ | ||
33 | #define PTP_STSR 0x0708 /* System Time – Seconds Regr */ | ||
34 | #define PTP_STNSR 0x070C /* System Time – Nanoseconds Reg */ | ||
35 | #define PTP_STSUR 0x0710 /* System Time – Seconds Update Reg */ | ||
36 | #define PTP_STNSUR 0x0714 /* System Time – Nanoseconds Update Reg */ | ||
37 | #define PTP_TAR 0x0718 /* Timestamp Addend Reg */ | ||
38 | #define PTP_TTSR 0x071C /* Target Time Seconds Reg */ | ||
39 | #define PTP_TTNSR 0x0720 /* Target Time Nanoseconds Reg */ | ||
40 | #define PTP_STHWSR 0x0724 /* System Time - Higher Word Seconds Reg */ | ||
41 | #define PTP_TSR 0x0728 /* Timestamp Status */ | ||
42 | |||
43 | #define PTP_STNSUR_ADDSUB_SHIFT 31 | ||
44 | |||
45 | /* PTP TCR defines */ | ||
46 | #define PTP_TCR_TSENA 0x00000001 /* Timestamp Enable */ | ||
47 | #define PTP_TCR_TSCFUPDT 0x00000002 /* Timestamp Fine/Coarse Update */ | ||
48 | #define PTP_TCR_TSINIT 0x00000004 /* Timestamp Initialize */ | ||
49 | #define PTP_TCR_TSUPDT 0x00000008 /* Timestamp Update */ | ||
50 | /* Timestamp Interrupt Trigger Enable */ | ||
51 | #define PTP_TCR_TSTRIG 0x00000010 | ||
52 | #define PTP_TCR_TSADDREG 0x00000020 /* Addend Reg Update */ | ||
53 | #define PTP_TCR_TSENALL 0x00000100 /* Enable Timestamp for All Frames */ | ||
54 | /* Timestamp Digital or Binary Rollover Control */ | ||
55 | #define PTP_TCR_TSCTRLSSR 0x00000200 | ||
56 | |||
57 | /* Enable PTP packet Processing for Version 2 Format */ | ||
58 | #define PTP_TCR_TSVER2ENA 0x00000400 | ||
59 | /* Enable Processing of PTP over Ethernet Frames */ | ||
60 | #define PTP_TCR_TSIPENA 0x00000800 | ||
61 | /* Enable Processing of PTP Frames Sent over IPv6-UDP */ | ||
62 | #define PTP_TCR_TSIPV6ENA 0x00001000 | ||
63 | /* Enable Processing of PTP Frames Sent over IPv4-UDP */ | ||
64 | #define PTP_TCR_TSIPV4ENA 0x00002000 | ||
65 | /* Enable Timestamp Snapshot for Event Messages */ | ||
66 | #define PTP_TCR_TSEVNTENA 0x00004000 | ||
67 | /* Enable Snapshot for Messages Relevant to Master */ | ||
68 | #define PTP_TCR_TSMSTRENA 0x00008000 | ||
69 | /* Select PTP packets for Taking Snapshots */ | ||
70 | #define PTP_TCR_SNAPTYPSEL_1 0x00010000 | ||
71 | /* Enable MAC address for PTP Frame Filtering */ | ||
72 | #define PTP_TCR_TSENMACADDR 0x00040000 | ||
73 | |||
74 | #endif /* __STMMAC_PTP_H__ */ | ||