diff options
author | Luwei Zhou <b45643@freescale.com> | 2013-08-15 01:45:23 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:01:15 -0400 |
commit | 244281f46581a1716e99b6b6fe0aaf019e0d8b65 (patch) | |
tree | 5a693027e514981ca625fb4c92d2ea9bbbf87590 /drivers/net/ethernet/freescale | |
parent | 67b6e0869cbfd7ebbc8f91336f4131f5559eacd3 (diff) |
ENGR00275371 net: fec: PTP: Add ptp support for IXXAT stack
These patch add ptp support for IXXAT stack.
Cherry picked from commit 1c8839574ac87826f53b96f987975a9bb1d72915.
Signed-off-by: Luwei Zhou <B45643@freescale.com>
Diffstat (limited to 'drivers/net/ethernet/freescale')
-rw-r--r-- | drivers/net/ethernet/freescale/fec.h | 112 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 55 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_ptp.c | 641 |
3 files changed, 753 insertions, 55 deletions
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index ea5c60d84889..888ff237338d 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h | |||
@@ -16,6 +16,9 @@ | |||
16 | #include <linux/clocksource.h> | 16 | #include <linux/clocksource.h> |
17 | #include <linux/net_tstamp.h> | 17 | #include <linux/net_tstamp.h> |
18 | #include <linux/ptp_clock_kernel.h> | 18 | #include <linux/ptp_clock_kernel.h> |
19 | #include <linux/netdevice.h> | ||
20 | #include <linux/etherdevice.h> | ||
21 | #include <linux/circ_buf.h> | ||
19 | 22 | ||
20 | #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ | 23 | #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ |
21 | defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ | 24 | defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ |
@@ -256,6 +259,98 @@ struct bufdesc_ex { | |||
256 | #define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR) | 259 | #define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR) |
257 | #define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR) | 260 | #define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR) |
258 | 261 | ||
262 | #define FALSE 0 | ||
263 | #define TRUE 1 | ||
264 | |||
265 | /* IEEE 1588 definition */ | ||
266 | #define FEC_T_PERIOD_ONE_SEC 0x3B9ACA00 | ||
267 | |||
268 | #define DEFAULT_PTP_RX_BUF_SZ 64 | ||
269 | #define DEFAULT_PTP_TX_BUF_SZ 64 | ||
270 | |||
271 | /* 1588stack API defines */ | ||
272 | #define PTP_ENBL_TXTS_IOCTL SIOCDEVPRIVATE | ||
273 | #define PTP_DSBL_TXTS_IOCTL (SIOCDEVPRIVATE + 1) | ||
274 | #define PTP_ENBL_RXTS_IOCTL (SIOCDEVPRIVATE + 2) | ||
275 | #define PTP_DSBL_RXTS_IOCTL (SIOCDEVPRIVATE + 3) | ||
276 | #define PTP_GET_TX_TIMESTAMP (SIOCDEVPRIVATE + 4) | ||
277 | #define PTP_GET_RX_TIMESTAMP (SIOCDEVPRIVATE + 5) | ||
278 | #define PTP_SET_RTC_TIME (SIOCDEVPRIVATE + 6) | ||
279 | #define PTP_GET_CURRENT_TIME (SIOCDEVPRIVATE + 7) | ||
280 | #define PTP_SET_COMPENSATION (SIOCDEVPRIVATE + 9) | ||
281 | #define PTP_GET_ORIG_COMP (SIOCDEVPRIVATE + 10) | ||
282 | #define PTP_FLUSH_TIMESTAMP (SIOCDEVPRIVATE + 11) | ||
283 | |||
284 | /* IEEE1588 ptp head format */ | ||
285 | #define PTP_CTRL_OFFS 0x52 | ||
286 | #define PTP_SOURCE_PORT_LENGTH 10 | ||
287 | #define PTP_HEADER_SEQ_OFFS 30 | ||
288 | #define PTP_HEADER_CTL_OFFS 32 | ||
289 | #define PTP_SPID_OFFS 20 | ||
290 | #define PTP_HEADER_SZE 34 | ||
291 | #define PTP_EVENT_PORT 0x013F | ||
292 | |||
293 | #define FEC_VLAN_TAG_LEN 0x04 | ||
294 | #define FEC_ETHTYPE_LEN 0x02 | ||
295 | |||
296 | /* 1588-2008 network protocol enumeration values */ | ||
297 | #define FEC_PTP_PROT_IPV4 1 | ||
298 | #define FEC_PTP_PROT_IPV6 2 | ||
299 | #define FEC_PTP_PROT_802_3 3 | ||
300 | #define FEC_PTP_PROT_DONTCARE 0xFFFF | ||
301 | #define FEC_PACKET_TYPE_UDP 0x11 | ||
302 | |||
303 | #define FEC_PTP_ORIG_COMP 0x15555555 | ||
304 | #define FEC_PTP_SPINNER_2 2 | ||
305 | #define FEC_PTP_SPINNER_4 4 | ||
306 | |||
307 | /* PTP standard time representation structure */ | ||
308 | struct ptp_time{ | ||
309 | u64 sec; /* seconds */ | ||
310 | u32 nsec; /* nanoseconds */ | ||
311 | }; | ||
312 | |||
313 | /* struct needed to identify a timestamp */ | ||
314 | struct fec_ptp_ident { | ||
315 | u8 version; | ||
316 | u8 message_type; | ||
317 | u16 netw_prot; | ||
318 | u16 seq_id; | ||
319 | u8 spid[10]; | ||
320 | }; | ||
321 | |||
322 | /* interface for PTP driver command GET_TX_TIME */ | ||
323 | struct fec_ptp_ts_data { | ||
324 | struct fec_ptp_ident ident; | ||
325 | /* PTP timestamp */ | ||
326 | struct ptp_time ts; | ||
327 | }; | ||
328 | |||
329 | /* circular buffer for ptp timestamps over ioctl */ | ||
330 | struct fec_ptp_circular { | ||
331 | int front; | ||
332 | int end; | ||
333 | int size; | ||
334 | struct fec_ptp_ts_data *data_buf; | ||
335 | }; | ||
336 | |||
337 | /* interface for PTP driver command SET_RTC_TIME/GET_CURRENT_TIME */ | ||
338 | struct ptp_rtc_time { | ||
339 | struct ptp_time rtc_time; | ||
340 | }; | ||
341 | |||
342 | /* interface for PTP driver command SET_COMPENSATION */ | ||
343 | struct ptp_set_comp { | ||
344 | u32 drift; | ||
345 | bool o_ops; | ||
346 | u32 freq_compensation; | ||
347 | }; | ||
348 | |||
349 | struct ptp_time_correct { | ||
350 | u32 corr_period; | ||
351 | u32 corr_inc; | ||
352 | }; | ||
353 | |||
259 | struct fec_enet_delayed_work { | 354 | struct fec_enet_delayed_work { |
260 | struct delayed_work delay_work; | 355 | struct delayed_work delay_work; |
261 | bool timeout; | 356 | bool timeout; |
@@ -312,13 +407,19 @@ struct fec_enet_private { | |||
312 | int speed; | 407 | int speed; |
313 | struct completion mdio_done; | 408 | struct completion mdio_done; |
314 | int irq[FEC_IRQ_NUM]; | 409 | int irq[FEC_IRQ_NUM]; |
410 | /* HW timestamping over ioctl enabled flag */ | ||
411 | int hwts_tx_en_ioctl; | ||
412 | int hwts_rx_en_ioctl; | ||
413 | struct fec_ptp_circular tx_timestamps; | ||
414 | struct fec_ptp_circular rx_timestamps; | ||
415 | u64 prtc; | ||
315 | int bufdesc_ex; | 416 | int bufdesc_ex; |
316 | int pause_flag; | 417 | int pause_flag; |
317 | 418 | ||
318 | struct napi_struct napi; | 419 | struct napi_struct napi; |
319 | int csum_flags; | 420 | int csum_flags; |
320 | 421 | ||
321 | int phy_reset_gpio; | 422 | int phy_reset_gpio; |
322 | 423 | ||
323 | struct ptp_clock *ptp_clock; | 424 | struct ptp_clock *ptp_clock; |
324 | struct ptp_clock_info ptp_caps; | 425 | struct ptp_clock_info ptp_caps; |
@@ -339,6 +440,15 @@ struct fec_enet_private { | |||
339 | void fec_ptp_init(struct platform_device *pdev); | 440 | void fec_ptp_init(struct platform_device *pdev); |
340 | void fec_ptp_start_cyclecounter(struct net_device *ndev); | 441 | void fec_ptp_start_cyclecounter(struct net_device *ndev); |
341 | int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd); | 442 | int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd); |
443 | void fec_ptp_cleanup(struct fec_enet_private *priv); | ||
444 | void fec_ptp_stop(struct net_device *ndev); | ||
445 | int fec_ptp_do_txstamp(struct sk_buff *skb); | ||
446 | void fec_ptp_store_txstamp(struct fec_enet_private *priv, | ||
447 | struct sk_buff *skb, | ||
448 | struct bufdesc *bdp); | ||
449 | void fec_ptp_store_rxstamp(struct fec_enet_private *priv, | ||
450 | struct sk_buff *skb, | ||
451 | struct bufdesc *bdp); | ||
342 | 452 | ||
343 | /****************************************************************************/ | 453 | /****************************************************************************/ |
344 | #endif /* FEC_H */ | 454 | #endif /* FEC_H */ |
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 86c35a8817cf..18a76fb8e595 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -197,6 +197,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
197 | #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ | 197 | #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ |
198 | #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ | 198 | #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ |
199 | #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ | 199 | #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ |
200 | #define FEC_ENET_TS_AVAIL ((uint)0x00010000) | ||
201 | #define FEC_ENET_TS_TIMER ((uint)0x00008000) | ||
200 | 202 | ||
201 | #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) | 203 | #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) |
202 | #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) | 204 | #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) |
@@ -369,7 +371,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
369 | struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; | 371 | struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; |
370 | ebdp->cbd_bdu = 0; | 372 | ebdp->cbd_bdu = 0; |
371 | if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && | 373 | if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && |
372 | fep->hwts_tx_en)) { | 374 | fep->hwts_tx_en) || unlikely(fep->hwts_tx_en_ioctl && |
375 | fec_ptp_do_txstamp(skb))) { | ||
373 | ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT); | 376 | ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT); |
374 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; | 377 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; |
375 | } else { | 378 | } else { |
@@ -645,8 +648,13 @@ fec_restart(struct net_device *ndev, int duplex) | |||
645 | writel(ecntl, fep->hwp + FEC_ECNTRL); | 648 | writel(ecntl, fep->hwp + FEC_ECNTRL); |
646 | writel(0, fep->hwp + FEC_R_DES_ACTIVE); | 649 | writel(0, fep->hwp + FEC_R_DES_ACTIVE); |
647 | 650 | ||
648 | if (fep->bufdesc_ex) | 651 | if (fep->bufdesc_ex) { |
649 | fec_ptp_start_cyclecounter(ndev); | 652 | fec_ptp_start_cyclecounter(ndev); |
653 | /* Enable interrupts we wish to service */ | ||
654 | writel(FEC_DEFAULT_IMASK | FEC_ENET_TS_AVAIL | | ||
655 | FEC_ENET_TS_TIMER, fep->hwp + FEC_IMASK); | ||
656 | } else | ||
657 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | ||
650 | 658 | ||
651 | /* Enable interrupts we wish to service */ | 659 | /* Enable interrupts we wish to service */ |
652 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | 660 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); |
@@ -681,6 +689,11 @@ fec_stop(struct net_device *ndev) | |||
681 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | 689 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
682 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | 690 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); |
683 | 691 | ||
692 | if (fep->bufdesc_ex && (fep->hwts_tx_en_ioctl || | ||
693 | fep->hwts_rx_en_ioctl || fep->hwts_tx_en || | ||
694 | fep->hwts_rx_en)) | ||
695 | fec_ptp_stop(ndev); | ||
696 | |||
684 | /* We have to keep ENET enabled to have MII interrupt stay working */ | 697 | /* We have to keep ENET enabled to have MII interrupt stay working */ |
685 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | 698 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { |
686 | writel(2, fep->hwp + FEC_ECNTRL); | 699 | writel(2, fep->hwp + FEC_ECNTRL); |
@@ -775,8 +788,8 @@ fec_enet_tx(struct net_device *ndev) | |||
775 | ndev->stats.tx_bytes += bdp->cbd_datlen; | 788 | ndev->stats.tx_bytes += bdp->cbd_datlen; |
776 | } | 789 | } |
777 | 790 | ||
778 | if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && | 791 | if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && |
779 | fep->bufdesc_ex) { | 792 | fep->hwts_tx_en) && fep->bufdesc_ex) { |
780 | struct skb_shared_hwtstamps shhwtstamps; | 793 | struct skb_shared_hwtstamps shhwtstamps; |
781 | unsigned long flags; | 794 | unsigned long flags; |
782 | struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; | 795 | struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; |
@@ -787,7 +800,8 @@ fec_enet_tx(struct net_device *ndev) | |||
787 | timecounter_cyc2time(&fep->tc, ebdp->ts)); | 800 | timecounter_cyc2time(&fep->tc, ebdp->ts)); |
788 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); | 801 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
789 | skb_tstamp_tx(skb, &shhwtstamps); | 802 | skb_tstamp_tx(skb, &shhwtstamps); |
790 | } | 803 | } else if (unlikely(fep->hwts_tx_en_ioctl) && fep->bufdesc_ex) |
804 | fec_ptp_store_txstamp(fep, skb, bdp); | ||
791 | 805 | ||
792 | if (status & BD_ENET_TX_READY) | 806 | if (status & BD_ENET_TX_READY) |
793 | netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n"); | 807 | netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n"); |
@@ -944,8 +958,6 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
944 | data + payload_offset, | 958 | data + payload_offset, |
945 | pkt_len - 4 - (2 * ETH_ALEN)); | 959 | pkt_len - 4 - (2 * ETH_ALEN)); |
946 | 960 | ||
947 | skb->protocol = eth_type_trans(skb, ndev); | ||
948 | |||
949 | /* Get receive timestamp from the skb */ | 961 | /* Get receive timestamp from the skb */ |
950 | if (fep->hwts_rx_en && fep->bufdesc_ex) { | 962 | if (fep->hwts_rx_en && fep->bufdesc_ex) { |
951 | struct skb_shared_hwtstamps *shhwtstamps = | 963 | struct skb_shared_hwtstamps *shhwtstamps = |
@@ -958,8 +970,11 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
958 | shhwtstamps->hwtstamp = ns_to_ktime( | 970 | shhwtstamps->hwtstamp = ns_to_ktime( |
959 | timecounter_cyc2time(&fep->tc, ebdp->ts)); | 971 | timecounter_cyc2time(&fep->tc, ebdp->ts)); |
960 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); | 972 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
961 | } | 973 | } else if (unlikely(fep->hwts_rx_en_ioctl) && |
974 | fep->bufdesc_ex) | ||
975 | fec_ptp_store_rxstamp(fep, skb, bdp); | ||
962 | 976 | ||
977 | skb->protocol = eth_type_trans(skb, ndev); | ||
963 | if (fep->bufdesc_ex && | 978 | if (fep->bufdesc_ex && |
964 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { | 979 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { |
965 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { | 980 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { |
@@ -1037,6 +1052,12 @@ fec_enet_interrupt(int irq, void *dev_id) | |||
1037 | } | 1052 | } |
1038 | } | 1053 | } |
1039 | 1054 | ||
1055 | if ((int_events & FEC_ENET_TS_TIMER) && fep->bufdesc_ex) { | ||
1056 | ret = IRQ_HANDLED; | ||
1057 | if (fep->hwts_tx_en_ioctl || fep->hwts_rx_en_ioctl) | ||
1058 | fep->prtc++; | ||
1059 | } | ||
1060 | |||
1040 | if (int_events & FEC_ENET_MII) { | 1061 | if (int_events & FEC_ENET_MII) { |
1041 | ret = IRQ_HANDLED; | 1062 | ret = IRQ_HANDLED; |
1042 | complete(&fep->mdio_done); | 1063 | complete(&fep->mdio_done); |
@@ -1656,8 +1677,11 @@ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) | |||
1656 | if (!phydev) | 1677 | if (!phydev) |
1657 | return -ENODEV; | 1678 | return -ENODEV; |
1658 | 1679 | ||
1659 | if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex) | 1680 | if (((cmd == SIOCSHWTSTAMP) || ((cmd >= PTP_ENBL_TXTS_IOCTL) && |
1681 | (cmd <= PTP_FLUSH_TIMESTAMP))) && fep->bufdesc_ex) | ||
1660 | return fec_ptp_ioctl(ndev, rq, cmd); | 1682 | return fec_ptp_ioctl(ndev, rq, cmd); |
1683 | else if (fep->bufdesc_ex) | ||
1684 | return -ENODEV; | ||
1661 | 1685 | ||
1662 | return phy_mii_ioctl(phydev, rq, cmd); | 1686 | return phy_mii_ioctl(phydev, rq, cmd); |
1663 | } | 1687 | } |
@@ -2255,7 +2279,10 @@ fec_drv_remove(struct platform_device *pdev) | |||
2255 | cancel_delayed_work_sync(&(fep->delay_work.delay_work)); | 2279 | cancel_delayed_work_sync(&(fep->delay_work.delay_work)); |
2256 | unregister_netdev(ndev); | 2280 | unregister_netdev(ndev); |
2257 | fec_enet_mii_remove(fep); | 2281 | fec_enet_mii_remove(fep); |
2258 | del_timer_sync(&fep->time_keep); | 2282 | if (fep->bufdesc_ex) { |
2283 | fec_ptp_cleanup(fep); | ||
2284 | del_timer_sync(&fep->time_keep); | ||
2285 | } | ||
2259 | for (i = 0; i < FEC_IRQ_NUM; i++) { | 2286 | for (i = 0; i < FEC_IRQ_NUM; i++) { |
2260 | int irq = platform_get_irq(pdev, i); | 2287 | int irq = platform_get_irq(pdev, i); |
2261 | if (irq > 0) | 2288 | if (irq > 0) |
@@ -2263,9 +2290,11 @@ fec_drv_remove(struct platform_device *pdev) | |||
2263 | } | 2290 | } |
2264 | if (fep->reg_phy) | 2291 | if (fep->reg_phy) |
2265 | regulator_disable(fep->reg_phy); | 2292 | regulator_disable(fep->reg_phy); |
2266 | clk_disable_unprepare(fep->clk_ptp); | 2293 | if (fep->bufdesc_ex) { |
2267 | if (fep->ptp_clock) | 2294 | clk_disable_unprepare(fep->clk_ptp); |
2268 | ptp_clock_unregister(fep->ptp_clock); | 2295 | if (fep->ptp_clock) |
2296 | ptp_clock_unregister(fep->ptp_clock); | ||
2297 | } | ||
2269 | clk_disable_unprepare(fep->clk_enet_out); | 2298 | clk_disable_unprepare(fep->clk_enet_out); |
2270 | clk_disable_unprepare(fep->clk_ahb); | 2299 | clk_disable_unprepare(fep->clk_ahb); |
2271 | clk_disable_unprepare(fep->clk_ipg); | 2300 | clk_disable_unprepare(fep->clk_ipg); |
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 5007e4f9fff9..06c679ebcd9c 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Fast Ethernet Controller (ENET) PTP driver for MX6x. | 2 | * Fast Ethernet Controller (ENET) PTP driver for MX6x. |
3 | * | 3 | * |
4 | * Copyright (C) 2012 Freescale Semiconductor, Inc. | 4 | * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 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, | 7 | * under the terms and conditions of the GNU General Public License, |
@@ -37,6 +37,9 @@ | |||
37 | #include <linux/workqueue.h> | 37 | #include <linux/workqueue.h> |
38 | #include <linux/bitops.h> | 38 | #include <linux/bitops.h> |
39 | #include <linux/io.h> | 39 | #include <linux/io.h> |
40 | #include <linux/vmalloc.h> | ||
41 | #include <linux/ip.h> | ||
42 | #include <linux/udp.h> | ||
40 | #include <linux/irq.h> | 43 | #include <linux/irq.h> |
41 | #include <linux/clk.h> | 44 | #include <linux/clk.h> |
42 | #include <linux/platform_device.h> | 45 | #include <linux/platform_device.h> |
@@ -49,6 +52,7 @@ | |||
49 | 52 | ||
50 | #include "fec.h" | 53 | #include "fec.h" |
51 | 54 | ||
55 | |||
52 | /* FEC 1588 register bits */ | 56 | /* FEC 1588 register bits */ |
53 | #define FEC_T_CTRL_SLAVE 0x00002000 | 57 | #define FEC_T_CTRL_SLAVE 0x00002000 |
54 | #define FEC_T_CTRL_CAPTURE 0x00000800 | 58 | #define FEC_T_CTRL_CAPTURE 0x00000800 |
@@ -71,6 +75,446 @@ | |||
71 | #define FEC_TS_TIMESTAMP 0x418 | 75 | #define FEC_TS_TIMESTAMP 0x418 |
72 | 76 | ||
73 | #define FEC_CC_MULT (1 << 31) | 77 | #define FEC_CC_MULT (1 << 31) |
78 | |||
79 | /* Alloc the ring resource */ | ||
80 | static int fec_ptp_init_circ(struct fec_ptp_circular *buf, int size) | ||
81 | { | ||
82 | buf->data_buf = (struct fec_ptp_ts_data *) | ||
83 | vmalloc(size * sizeof(struct fec_ptp_ts_data)); | ||
84 | |||
85 | if (!buf->data_buf) | ||
86 | return 1; | ||
87 | buf->front = 0; | ||
88 | buf->end = 0; | ||
89 | buf->size = size; | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static inline int fec_ptp_calc_index(int size, int curr_index, int offset) | ||
94 | { | ||
95 | return (curr_index + offset) % size; | ||
96 | } | ||
97 | |||
98 | static int fec_ptp_is_empty(struct fec_ptp_circular *buf) | ||
99 | { | ||
100 | return (buf->front == buf->end); | ||
101 | } | ||
102 | |||
103 | static int fec_ptp_nelems(struct fec_ptp_circular *buf) | ||
104 | { | ||
105 | const int front = buf->front; | ||
106 | const int end = buf->end; | ||
107 | const int size = buf->size; | ||
108 | int n_items; | ||
109 | |||
110 | if (end > front) | ||
111 | n_items = end - front; | ||
112 | else if (end < front) | ||
113 | n_items = size - (front - end); | ||
114 | else | ||
115 | n_items = 0; | ||
116 | |||
117 | return n_items; | ||
118 | } | ||
119 | |||
120 | static int fec_ptp_is_full(struct fec_ptp_circular *buf) | ||
121 | { | ||
122 | if (fec_ptp_nelems(buf) == (buf->size - 1)) | ||
123 | return 1; | ||
124 | else | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int fec_ptp_insert(struct fec_ptp_circular *ptp_buf, | ||
129 | struct fec_ptp_ts_data *data) | ||
130 | { | ||
131 | struct fec_ptp_ts_data *tmp; | ||
132 | |||
133 | if (fec_ptp_is_full(ptp_buf)) | ||
134 | ptp_buf->end = fec_ptp_calc_index(ptp_buf->size, | ||
135 | ptp_buf->end, 1); | ||
136 | |||
137 | tmp = (ptp_buf->data_buf + ptp_buf->end); | ||
138 | memcpy(tmp, data, sizeof(struct fec_ptp_ts_data)); | ||
139 | ptp_buf->end = fec_ptp_calc_index(ptp_buf->size, ptp_buf->end, 1); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int fec_ptp_find_and_remove(struct fec_ptp_circular *ptp_buf, | ||
145 | struct fec_ptp_ident *ident, struct ptp_time *ts) | ||
146 | { | ||
147 | int i; | ||
148 | int size = ptp_buf->size, end = ptp_buf->end; | ||
149 | struct fec_ptp_ident *tmp_ident; | ||
150 | |||
151 | if (fec_ptp_is_empty(ptp_buf)) | ||
152 | return 1; | ||
153 | |||
154 | i = ptp_buf->front; | ||
155 | while (i != end) { | ||
156 | tmp_ident = &(ptp_buf->data_buf + i)->ident; | ||
157 | if (tmp_ident->version == ident->version) { | ||
158 | if (tmp_ident->message_type == ident->message_type) { | ||
159 | if ((tmp_ident->netw_prot == ident->netw_prot) | ||
160 | || (ident->netw_prot == | ||
161 | FEC_PTP_PROT_DONTCARE)) { | ||
162 | if (tmp_ident->seq_id == | ||
163 | ident->seq_id) { | ||
164 | int ret = | ||
165 | memcmp(tmp_ident->spid, | ||
166 | ident->spid, | ||
167 | PTP_SOURCE_PORT_LENGTH); | ||
168 | if (0 == ret) | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | /* get next */ | ||
175 | i = fec_ptp_calc_index(size, i, 1); | ||
176 | } | ||
177 | |||
178 | /* not found ? */ | ||
179 | if (i == end) { | ||
180 | /* buffer full ? */ | ||
181 | if (fec_ptp_is_full(ptp_buf)) | ||
182 | /* drop one in front */ | ||
183 | ptp_buf->front = | ||
184 | fec_ptp_calc_index(size, ptp_buf->front, 1); | ||
185 | |||
186 | return 1; | ||
187 | } | ||
188 | *ts = (ptp_buf->data_buf + i)->ts; | ||
189 | ptp_buf->front = fec_ptp_calc_index(size, ptp_buf->front, 1); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | /* 1588 Module intialization */ | ||
195 | void fec_ptp_start(struct net_device *ndev) | ||
196 | { | ||
197 | struct fec_enet_private *fep = netdev_priv(ndev); | ||
198 | unsigned long flags; | ||
199 | int inc; | ||
200 | |||
201 | inc = FEC_T_PERIOD_ONE_SEC / clk_get_rate(fep->clk_ptp); | ||
202 | |||
203 | spin_lock_irqsave(&fep->tmreg_lock, flags); | ||
204 | /* Select 1588 Timer source and enable module for starting Tmr Clock */ | ||
205 | writel(FEC_T_CTRL_RESTART, fep->hwp + FEC_ATIME_CTRL); | ||
206 | writel(inc << FEC_T_INC_OFFSET, | ||
207 | fep->hwp + FEC_ATIME_INC); | ||
208 | writel(FEC_T_PERIOD_ONE_SEC, fep->hwp + FEC_ATIME_EVT_PERIOD); | ||
209 | /* start counter */ | ||
210 | writel(FEC_T_CTRL_PERIOD_RST | FEC_T_CTRL_ENABLE, | ||
211 | fep->hwp + FEC_ATIME_CTRL); | ||
212 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); | ||
213 | } | ||
214 | |||
215 | /* Cleanup routine for 1588 module. | ||
216 | * When PTP is disabled this routing is called */ | ||
217 | void fec_ptp_stop(struct net_device *ndev) | ||
218 | { | ||
219 | struct fec_enet_private *priv = netdev_priv(ndev); | ||
220 | |||
221 | writel(0, priv->hwp + FEC_ATIME_CTRL); | ||
222 | writel(FEC_T_CTRL_RESTART, priv->hwp + FEC_ATIME_CTRL); | ||
223 | } | ||
224 | |||
225 | static void fec_get_curr_cnt(struct fec_enet_private *priv, | ||
226 | struct ptp_rtc_time *curr_time) | ||
227 | { | ||
228 | u32 tempval; | ||
229 | |||
230 | tempval = readl(priv->hwp + FEC_ATIME_CTRL); | ||
231 | tempval |= FEC_T_CTRL_CAPTURE; | ||
232 | |||
233 | writel(tempval, priv->hwp + FEC_ATIME_CTRL); | ||
234 | curr_time->rtc_time.nsec = readl(priv->hwp + FEC_ATIME); | ||
235 | curr_time->rtc_time.sec = priv->prtc; | ||
236 | |||
237 | writel(tempval, priv->hwp + FEC_ATIME_CTRL); | ||
238 | tempval = readl(priv->hwp + FEC_ATIME); | ||
239 | |||
240 | if (tempval < curr_time->rtc_time.nsec) { | ||
241 | curr_time->rtc_time.nsec = tempval; | ||
242 | curr_time->rtc_time.sec = priv->prtc; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* Set the 1588 timer counter registers */ | ||
247 | static void fec_set_1588cnt(struct fec_enet_private *priv, | ||
248 | struct ptp_rtc_time *fec_time) | ||
249 | { | ||
250 | u32 tempval; | ||
251 | unsigned long flags; | ||
252 | |||
253 | spin_lock_irqsave(&priv->tmreg_lock, flags); | ||
254 | priv->prtc = fec_time->rtc_time.sec; | ||
255 | |||
256 | tempval = fec_time->rtc_time.nsec; | ||
257 | writel(tempval, priv->hwp + FEC_ATIME); | ||
258 | spin_unlock_irqrestore(&priv->tmreg_lock, flags); | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * Parse packets if they are PTP. | ||
263 | * The PTP header can be found in an IPv4, IPv6 or in an IEEE802.3 | ||
264 | * ethernet frame. The function returns the position of the PTP packet | ||
265 | * or NULL, if no PTP found | ||
266 | */ | ||
267 | u8 *fec_ptp_parse_packet(struct sk_buff *skb, u16 *eth_type) | ||
268 | { | ||
269 | u8 *position = skb->data + ETH_ALEN + ETH_ALEN; | ||
270 | u8 *ptp_loc = NULL; | ||
271 | |||
272 | *eth_type = *((u16 *)position); | ||
273 | /* Check if outer vlan tag is here */ | ||
274 | if (ntohs(*eth_type) == ETH_P_8021Q) { | ||
275 | position += FEC_VLAN_TAG_LEN; | ||
276 | *eth_type = *((u16 *)position); | ||
277 | } | ||
278 | |||
279 | /* set position after ethertype */ | ||
280 | position += FEC_ETHTYPE_LEN; | ||
281 | if (ETH_P_1588 == ntohs(*eth_type)) { | ||
282 | ptp_loc = position; | ||
283 | /* IEEE1588 event message which needs timestamping */ | ||
284 | if ((ptp_loc[0] & 0xF) <= 3) { | ||
285 | if (skb->len >= | ||
286 | ((ptp_loc - skb->data) + PTP_HEADER_SZE)) | ||
287 | return ptp_loc; | ||
288 | } | ||
289 | } else if (ETH_P_IP == ntohs(*eth_type)) { | ||
290 | u8 *ip_header, *prot, *udp_header; | ||
291 | u8 ip_version, ip_hlen; | ||
292 | ip_header = position; | ||
293 | ip_version = ip_header[0] >> 4; /* correct IP version? */ | ||
294 | if (0x04 == ip_version) { /* IPv4 */ | ||
295 | prot = ip_header + 9; /* protocol */ | ||
296 | if (FEC_PACKET_TYPE_UDP == *prot) { | ||
297 | u16 udp_dstPort; | ||
298 | /* retrieve the size of the ip-header | ||
299 | * with the first byte of the ip-header: | ||
300 | * version ( 4 bits) + Internet header | ||
301 | * length (4 bits) | ||
302 | */ | ||
303 | ip_hlen = (*ip_header & 0xf) * 4; | ||
304 | udp_header = ip_header + ip_hlen; | ||
305 | udp_dstPort = *((u16 *)(udp_header + 2)); | ||
306 | /* check the destination port address | ||
307 | * ( 319 (0x013F) = PTP event port ) | ||
308 | */ | ||
309 | if (ntohs(udp_dstPort) == PTP_EVENT_PORT) { | ||
310 | ptp_loc = udp_header + 8; | ||
311 | /* long enough ? */ | ||
312 | if (skb->len >= ((ptp_loc - skb->data) | ||
313 | + PTP_HEADER_SZE)) | ||
314 | return ptp_loc; | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | } else if (ETH_P_IPV6 == ntohs(*eth_type)) { | ||
319 | u8 *ip_header, *udp_header, *prot; | ||
320 | u8 ip_version; | ||
321 | ip_header = position; | ||
322 | ip_version = ip_header[0] >> 4; | ||
323 | if (0x06 == ip_version) { | ||
324 | prot = ip_header + 6; | ||
325 | if (FEC_PACKET_TYPE_UDP == *prot) { | ||
326 | u16 udp_dstPort; | ||
327 | udp_header = ip_header + 40; | ||
328 | udp_dstPort = *((u16 *)(udp_header + 2)); | ||
329 | /* check the destination port address | ||
330 | * ( 319 (0x013F) = PTP event port ) | ||
331 | */ | ||
332 | if (ntohs(udp_dstPort) == PTP_EVENT_PORT) { | ||
333 | ptp_loc = udp_header + 8; | ||
334 | /* long enough ? */ | ||
335 | if (skb->len >= ((ptp_loc - skb->data) | ||
336 | + PTP_HEADER_SZE)) | ||
337 | return ptp_loc; | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | return NULL; /* no PTP frame */ | ||
344 | } | ||
345 | |||
346 | /* Set the BD to ptp */ | ||
347 | int fec_ptp_do_txstamp(struct sk_buff *skb) | ||
348 | { | ||
349 | u8 *ptp_loc; | ||
350 | u16 eth_type; | ||
351 | |||
352 | ptp_loc = fec_ptp_parse_packet(skb, ð_type); | ||
353 | if (ptp_loc != NULL) | ||
354 | return 1; | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | void fec_ptp_store_txstamp(struct fec_enet_private *priv, | ||
360 | struct sk_buff *skb, | ||
361 | struct bufdesc *bdp) | ||
362 | { | ||
363 | struct fec_ptp_ts_data tmp_tx_time; | ||
364 | struct bufdesc_ex *bdp_ex = NULL; | ||
365 | u8 *ptp_loc; | ||
366 | u16 eth_type; | ||
367 | |||
368 | bdp_ex = container_of(bdp, struct bufdesc_ex, desc); | ||
369 | ptp_loc = fec_ptp_parse_packet(skb, ð_type); | ||
370 | if (ptp_loc != NULL) { | ||
371 | /* store identification data */ | ||
372 | switch (ntohs(eth_type)) { | ||
373 | case ETH_P_IP: | ||
374 | tmp_tx_time.ident.netw_prot = FEC_PTP_PROT_IPV4; | ||
375 | break; | ||
376 | case ETH_P_IPV6: | ||
377 | tmp_tx_time.ident.netw_prot = FEC_PTP_PROT_IPV6; | ||
378 | break; | ||
379 | case ETH_P_1588: | ||
380 | tmp_tx_time.ident.netw_prot = FEC_PTP_PROT_802_3; | ||
381 | break; | ||
382 | default: | ||
383 | return; | ||
384 | } | ||
385 | tmp_tx_time.ident.version = (*(ptp_loc + 1)) & 0X0F; | ||
386 | tmp_tx_time.ident.message_type = (*(ptp_loc)) & 0x0F; | ||
387 | tmp_tx_time.ident.seq_id = | ||
388 | ntohs(*((u16 *)(ptp_loc + PTP_HEADER_SEQ_OFFS))); | ||
389 | memcpy(tmp_tx_time.ident.spid, &ptp_loc[PTP_SPID_OFFS], | ||
390 | PTP_SOURCE_PORT_LENGTH); | ||
391 | /* store tx timestamp */ | ||
392 | tmp_tx_time.ts.sec = priv->prtc; | ||
393 | tmp_tx_time.ts.nsec = bdp_ex->ts; | ||
394 | /* insert timestamp in circular buffer */ | ||
395 | fec_ptp_insert(&(priv->tx_timestamps), &tmp_tx_time); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | void fec_ptp_store_rxstamp(struct fec_enet_private *priv, | ||
400 | struct sk_buff *skb, | ||
401 | struct bufdesc *bdp) | ||
402 | { | ||
403 | struct fec_ptp_ts_data tmp_rx_time; | ||
404 | struct bufdesc_ex *bdp_ex = NULL; | ||
405 | u8 *ptp_loc; | ||
406 | u16 eth_type; | ||
407 | |||
408 | bdp_ex = container_of(bdp, struct bufdesc_ex, desc); | ||
409 | ptp_loc = fec_ptp_parse_packet(skb, ð_type); | ||
410 | if (ptp_loc != NULL) { | ||
411 | /* store identification data */ | ||
412 | tmp_rx_time.ident.version = (*(ptp_loc + 1)) & 0X0F; | ||
413 | tmp_rx_time.ident.message_type = (*(ptp_loc)) & 0x0F; | ||
414 | switch (ntohs(eth_type)) { | ||
415 | case ETH_P_IP: | ||
416 | tmp_rx_time.ident.netw_prot = FEC_PTP_PROT_IPV4; | ||
417 | break; | ||
418 | case ETH_P_IPV6: | ||
419 | tmp_rx_time.ident.netw_prot = FEC_PTP_PROT_IPV6; | ||
420 | break; | ||
421 | case ETH_P_1588: | ||
422 | tmp_rx_time.ident.netw_prot = FEC_PTP_PROT_802_3; | ||
423 | break; | ||
424 | default: | ||
425 | return; | ||
426 | } | ||
427 | tmp_rx_time.ident.seq_id = | ||
428 | ntohs(*((u16 *)(ptp_loc + PTP_HEADER_SEQ_OFFS))); | ||
429 | memcpy(tmp_rx_time.ident.spid, &ptp_loc[PTP_SPID_OFFS], | ||
430 | PTP_SOURCE_PORT_LENGTH); | ||
431 | /* store rx timestamp */ | ||
432 | tmp_rx_time.ts.sec = priv->prtc; | ||
433 | tmp_rx_time.ts.nsec = bdp_ex->ts; | ||
434 | |||
435 | /* insert timestamp in circular buffer */ | ||
436 | fec_ptp_insert(&(priv->rx_timestamps), &tmp_rx_time); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | |||
441 | static void fec_handle_ptpdrift(struct fec_enet_private *priv, | ||
442 | struct ptp_set_comp *comp, struct ptp_time_correct *ptc) | ||
443 | { | ||
444 | u32 ndrift; | ||
445 | u32 i, adj_inc, adj_period; | ||
446 | u32 tmp_current, tmp_winner; | ||
447 | u32 ptp_ts_clk, ptp_inc; | ||
448 | |||
449 | ptp_ts_clk = clk_get_rate(priv->clk_ptp); | ||
450 | ptp_inc = FEC_T_PERIOD_ONE_SEC / ptp_ts_clk; | ||
451 | |||
452 | ndrift = comp->drift; | ||
453 | |||
454 | if (ndrift == 0) { | ||
455 | ptc->corr_inc = 0; | ||
456 | ptc->corr_period = 0; | ||
457 | return; | ||
458 | } else if (ndrift >= ptp_ts_clk) { | ||
459 | ptc->corr_inc = (u32)(ndrift / ptp_ts_clk); | ||
460 | ptc->corr_period = 1; | ||
461 | return; | ||
462 | } else { | ||
463 | tmp_winner = 0xFFFFFFFF; | ||
464 | adj_inc = 1; | ||
465 | |||
466 | if (ndrift > (ptp_ts_clk / ptp_inc)) { | ||
467 | adj_inc = ptp_inc / FEC_PTP_SPINNER_2; | ||
468 | } else if (ndrift > (ptp_ts_clk / | ||
469 | (ptp_inc * FEC_PTP_SPINNER_4))) { | ||
470 | adj_inc = ptp_inc / FEC_PTP_SPINNER_4; | ||
471 | adj_period = FEC_PTP_SPINNER_2; | ||
472 | } else { | ||
473 | adj_inc = FEC_PTP_SPINNER_4; | ||
474 | adj_period = FEC_PTP_SPINNER_4; | ||
475 | } | ||
476 | |||
477 | for (i = 1; i < adj_inc; i++) { | ||
478 | tmp_current = (ptp_ts_clk * i) % ndrift; | ||
479 | if (tmp_current == 0) { | ||
480 | ptc->corr_inc = i; | ||
481 | ptc->corr_period = (u32)((ptp_ts_clk * | ||
482 | adj_period * i) / ndrift); | ||
483 | break; | ||
484 | } else if (tmp_current < tmp_winner) { | ||
485 | ptc->corr_inc = i; | ||
486 | ptc->corr_period = (u32)((ptp_ts_clk * | ||
487 | adj_period * i) / ndrift); | ||
488 | tmp_winner = tmp_current; | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | |||
494 | static void fec_set_drift(struct fec_enet_private *priv, | ||
495 | struct ptp_set_comp *comp) | ||
496 | { | ||
497 | struct ptp_time_correct tc; | ||
498 | u32 tmp, corr_ns; | ||
499 | u32 ptp_inc; | ||
500 | |||
501 | memset(&tc, 0, sizeof(struct ptp_time_correct)); | ||
502 | fec_handle_ptpdrift(priv, comp, &tc); | ||
503 | if (tc.corr_inc == 0) | ||
504 | return; | ||
505 | |||
506 | ptp_inc = FEC_T_PERIOD_ONE_SEC / clk_get_rate(priv->clk_ptp); | ||
507 | if (comp->o_ops == TRUE) | ||
508 | corr_ns = ptp_inc + tc.corr_inc; | ||
509 | else | ||
510 | corr_ns = ptp_inc - tc.corr_inc; | ||
511 | |||
512 | tmp = readl(priv->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK; | ||
513 | tmp |= corr_ns << FEC_T_INC_CORR_OFFSET; | ||
514 | writel(tmp, priv->hwp + FEC_ATIME_INC); | ||
515 | writel(tc.corr_period, priv->hwp + FEC_ATIME_CORR); | ||
516 | } | ||
517 | |||
74 | /** | 518 | /** |
75 | * fec_ptp_read - read raw cycle counter (to be used by time counter) | 519 | * fec_ptp_read - read raw cycle counter (to be used by time counter) |
76 | * @cc: the cyclecounter structure | 520 | * @cc: the cyclecounter structure |
@@ -106,18 +550,25 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev) | |||
106 | unsigned long flags; | 550 | unsigned long flags; |
107 | int inc; | 551 | int inc; |
108 | 552 | ||
109 | inc = 1000000000 / fep->cycle_speed; | 553 | inc = FEC_T_PERIOD_ONE_SEC / clk_get_rate(fep->clk_ptp); |
110 | 554 | ||
111 | /* grab the ptp lock */ | 555 | /* grab the ptp lock */ |
112 | spin_lock_irqsave(&fep->tmreg_lock, flags); | 556 | spin_lock_irqsave(&fep->tmreg_lock, flags); |
557 | writel(FEC_T_CTRL_RESTART, fep->hwp + FEC_ATIME_CTRL); | ||
113 | 558 | ||
114 | /* 1ns counter */ | 559 | /* 1ns counter */ |
115 | writel(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); | 560 | writel(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); |
116 | 561 | ||
117 | /* use free running count */ | 562 | if (fep->hwts_rx_en_ioctl || fep->hwts_tx_en_ioctl) { |
118 | writel(0, fep->hwp + FEC_ATIME_EVT_PERIOD); | 563 | writel(FEC_T_PERIOD_ONE_SEC, fep->hwp + FEC_ATIME_EVT_PERIOD); |
119 | 564 | /* start counter */ | |
120 | writel(FEC_T_CTRL_ENABLE, fep->hwp + FEC_ATIME_CTRL); | 565 | writel(FEC_T_CTRL_PERIOD_RST | FEC_T_CTRL_ENABLE, |
566 | fep->hwp + FEC_ATIME_CTRL); | ||
567 | } else if (fep->hwts_tx_en || fep->hwts_tx_en) { | ||
568 | /* use free running count */ | ||
569 | writel(0, fep->hwp + FEC_ATIME_EVT_PERIOD); | ||
570 | writel(FEC_T_CTRL_ENABLE, fep->hwp + FEC_ATIME_CTRL); | ||
571 | } | ||
121 | 572 | ||
122 | memset(&fep->cc, 0, sizeof(fep->cc)); | 573 | memset(&fep->cc, 0, sizeof(fep->cc)); |
123 | fep->cc.read = fec_ptp_read; | 574 | fep->cc.read = fec_ptp_read; |
@@ -277,50 +728,140 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, | |||
277 | int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) | 728 | int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) |
278 | { | 729 | { |
279 | struct fec_enet_private *fep = netdev_priv(ndev); | 730 | struct fec_enet_private *fep = netdev_priv(ndev); |
280 | |||
281 | struct hwtstamp_config config; | 731 | struct hwtstamp_config config; |
282 | 732 | struct ptp_rtc_time curr_time; | |
283 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) | 733 | struct ptp_time rx_time, tx_time; |
284 | return -EFAULT; | 734 | struct fec_ptp_ts_data p_ts; |
285 | 735 | struct fec_ptp_ts_data *p_ts_user; | |
286 | /* reserved for future extensions */ | 736 | struct ptp_set_comp p_comp; |
287 | if (config.flags) | 737 | u32 freq_compensation; |
288 | return -EINVAL; | 738 | int retval = 0; |
289 | 739 | ||
290 | switch (config.tx_type) { | 740 | switch (cmd) { |
291 | case HWTSTAMP_TX_OFF: | 741 | case SIOCSHWTSTAMP: |
742 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) | ||
743 | return -EFAULT; | ||
744 | |||
745 | /* reserved for future extensions */ | ||
746 | if (config.flags) | ||
747 | return -EINVAL; | ||
748 | |||
749 | switch (config.tx_type) { | ||
750 | case HWTSTAMP_TX_OFF: | ||
751 | fep->hwts_tx_en = 0; | ||
752 | break; | ||
753 | case HWTSTAMP_TX_ON: | ||
754 | fep->hwts_tx_en = 1; | ||
755 | fep->hwts_tx_en_ioctl = 0; | ||
756 | break; | ||
757 | default: | ||
758 | return -ERANGE; | ||
759 | } | ||
760 | |||
761 | switch (config.rx_filter) { | ||
762 | case HWTSTAMP_FILTER_NONE: | ||
763 | if (fep->hwts_rx_en) | ||
764 | fep->hwts_rx_en = 0; | ||
765 | config.rx_filter = HWTSTAMP_FILTER_NONE; | ||
766 | break; | ||
767 | |||
768 | default: | ||
769 | /* | ||
770 | * register RXMTRL must be set in order | ||
771 | * to do V1 packets, therefore it is not | ||
772 | * possible to time stamp both V1 Sync and | ||
773 | * Delay_Req messages and hardware does not support | ||
774 | * timestamping all packets => return error | ||
775 | */ | ||
776 | fep->hwts_rx_en = 1; | ||
777 | fep->hwts_rx_en_ioctl = 0; | ||
778 | config.rx_filter = HWTSTAMP_FILTER_ALL; | ||
779 | break; | ||
780 | } | ||
781 | |||
782 | fec_ptp_start_cyclecounter(ndev); | ||
783 | return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? | ||
784 | -EFAULT : 0; | ||
785 | break; | ||
786 | case PTP_ENBL_TXTS_IOCTL: | ||
787 | case PTP_ENBL_RXTS_IOCTL: | ||
788 | fep->hwts_rx_en_ioctl = 1; | ||
789 | fep->hwts_tx_en_ioctl = 1; | ||
790 | fep->hwts_rx_en = 0; | ||
292 | fep->hwts_tx_en = 0; | 791 | fep->hwts_tx_en = 0; |
792 | fec_ptp_start_cyclecounter(ndev); | ||
293 | break; | 793 | break; |
294 | case HWTSTAMP_TX_ON: | 794 | case PTP_DSBL_RXTS_IOCTL: |
295 | fep->hwts_tx_en = 1; | 795 | case PTP_DSBL_TXTS_IOCTL: |
796 | fep->hwts_rx_en_ioctl = 0; | ||
797 | fep->hwts_tx_en_ioctl = 0; | ||
296 | break; | 798 | break; |
297 | default: | 799 | case PTP_GET_RX_TIMESTAMP: |
298 | return -ERANGE; | 800 | p_ts_user = (struct fec_ptp_ts_data *)ifr->ifr_data; |
299 | } | 801 | if (0 != copy_from_user(&p_ts.ident, |
300 | 802 | &p_ts_user->ident, sizeof(p_ts.ident))) | |
301 | switch (config.rx_filter) { | 803 | return -EINVAL; |
302 | case HWTSTAMP_FILTER_NONE: | 804 | retval = fec_ptp_find_and_remove(&fep->rx_timestamps, |
303 | if (fep->hwts_rx_en) | 805 | &p_ts.ident, &rx_time); |
304 | fep->hwts_rx_en = 0; | 806 | if (retval == 0 && |
305 | config.rx_filter = HWTSTAMP_FILTER_NONE; | 807 | copy_to_user((void __user *)(&p_ts_user->ts), |
808 | &rx_time, sizeof(rx_time))) | ||
809 | return -EFAULT; | ||
306 | break; | 810 | break; |
307 | 811 | case PTP_GET_TX_TIMESTAMP: | |
308 | default: | 812 | p_ts_user = (struct fec_ptp_ts_data *)ifr->ifr_data; |
309 | /* | 813 | if (0 != copy_from_user(&p_ts.ident, |
310 | * register RXMTRL must be set in order to do V1 packets, | 814 | &p_ts_user->ident, sizeof(p_ts.ident))) |
311 | * therefore it is not possible to time stamp both V1 Sync and | 815 | return -EINVAL; |
312 | * Delay_Req messages and hardware does not support | 816 | retval = fec_ptp_find_and_remove(&fep->tx_timestamps, |
313 | * timestamping all packets => return error | 817 | &p_ts.ident, &tx_time); |
314 | */ | 818 | if (retval == 0 && |
315 | fep->hwts_rx_en = 1; | 819 | copy_to_user((void __user *)(&p_ts_user->ts), |
316 | config.rx_filter = HWTSTAMP_FILTER_ALL; | 820 | &tx_time, sizeof(tx_time))) |
821 | retval = -EFAULT; | ||
822 | break; | ||
823 | case PTP_GET_CURRENT_TIME: | ||
824 | fec_get_curr_cnt(fep, &curr_time); | ||
825 | if (0 != copy_to_user(ifr->ifr_data, | ||
826 | &(curr_time.rtc_time), | ||
827 | sizeof(struct ptp_time))) | ||
828 | return -EFAULT; | ||
829 | break; | ||
830 | case PTP_SET_RTC_TIME: | ||
831 | if (0 != copy_from_user(&(curr_time.rtc_time), | ||
832 | ifr->ifr_data, | ||
833 | sizeof(struct ptp_time))) | ||
834 | return -EINVAL; | ||
835 | fec_set_1588cnt(fep, &curr_time); | ||
317 | break; | 836 | break; |
837 | case PTP_FLUSH_TIMESTAMP: | ||
838 | /* reset tx-timestamping buffer */ | ||
839 | fep->tx_timestamps.front = 0; | ||
840 | fep->tx_timestamps.end = 0; | ||
841 | fep->tx_timestamps.size = (DEFAULT_PTP_TX_BUF_SZ + 1); | ||
842 | /* reset rx-timestamping buffer */ | ||
843 | fep->rx_timestamps.front = 0; | ||
844 | fep->rx_timestamps.end = 0; | ||
845 | fep->rx_timestamps.size = (DEFAULT_PTP_RX_BUF_SZ + 1); | ||
846 | break; | ||
847 | case PTP_SET_COMPENSATION: | ||
848 | if (0 != copy_from_user(&p_comp, ifr->ifr_data, | ||
849 | sizeof(struct ptp_set_comp))) | ||
850 | return -EINVAL; | ||
851 | fec_set_drift(fep, &p_comp); | ||
852 | break; | ||
853 | case PTP_GET_ORIG_COMP: | ||
854 | freq_compensation = FEC_PTP_ORIG_COMP; | ||
855 | if (copy_to_user(ifr->ifr_data, &freq_compensation, | ||
856 | sizeof(freq_compensation)) > 0) | ||
857 | return -EFAULT; | ||
858 | break; | ||
859 | default: | ||
860 | return -EINVAL; | ||
318 | } | 861 | } |
319 | 862 | ||
320 | return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? | 863 | return retval; |
321 | -EFAULT : 0; | ||
322 | } | 864 | } |
323 | |||
324 | /** | 865 | /** |
325 | * fec_time_keep - call timecounter_read every second to avoid timer overrun | 866 | * fec_time_keep - call timecounter_read every second to avoid timer overrun |
326 | * because ENET just support 32bit counter, will timeout in 4s | 867 | * because ENET just support 32bit counter, will timeout in 4s |
@@ -383,4 +924,22 @@ void fec_ptp_init(struct platform_device *pdev) | |||
383 | fep->ptp_clock = NULL; | 924 | fep->ptp_clock = NULL; |
384 | pr_err("ptp_clock_register failed\n"); | 925 | pr_err("ptp_clock_register failed\n"); |
385 | } | 926 | } |
927 | |||
928 | /* initialize circular buffer for tx timestamps */ | ||
929 | if (fec_ptp_init_circ(&(fep->tx_timestamps), | ||
930 | (DEFAULT_PTP_TX_BUF_SZ+1))) | ||
931 | pr_err("init tx circular buffer failed\n"); | ||
932 | /* initialize circular buffer for rx timestamps */ | ||
933 | if (fec_ptp_init_circ(&(fep->rx_timestamps), | ||
934 | (DEFAULT_PTP_RX_BUF_SZ+1))) | ||
935 | pr_err("init rx curcular buffer failed\n"); | ||
386 | } | 936 | } |
937 | |||
938 | void fec_ptp_cleanup(struct fec_enet_private *priv) | ||
939 | { | ||
940 | if (priv->tx_timestamps.data_buf) | ||
941 | vfree(priv->tx_timestamps.data_buf); | ||
942 | if (priv->rx_timestamps.data_buf) | ||
943 | vfree(priv->rx_timestamps.data_buf); | ||
944 | } | ||
945 | |||