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 | |
| 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')
| -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 | |||
