From 1468d507377042b2d647036ea81471e002aed61a Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 31 Oct 2014 16:00:34 +0800 Subject: dpaa_eth: add PTPd 1588 stack support Support PTPd 1588 stack by adding PTP 1588 clock using the dTSEC Signed-off-by: Yangbo Lu Change-Id: I4f6fb9a721cede75c6cf23560014b55b46c84fef Reviewed-on: http://git.am.freescale.net:8181/20295 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Mandy Lavi Reviewed-by: Richard Schmitt diff --git a/drivers/net/ethernet/freescale/dpa/Makefile b/drivers/net/ethernet/freescale/dpa/Makefile index e3389fc..929db39 100644 --- a/drivers/net/ethernet/freescale/dpa/Makefile +++ b/drivers/net/ethernet/freescale/dpa/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_FSL_DPAA_ETH) += fsl-mac.o fsl-dpa.o fsl-dpa-common.o \ fsl-dpa-base.o fsl-dpa-shared.o fsl-dpa-proxy.o fsl-dpa-macless.o \ fsl-dpa-generic.o obj-$(CONFIG_FSL_DPAA_OFFLINE_PORTS) += fsl-oh.o +obj-$(CONFIG_PTP_1588_CLOCK_DPAA) += dpaa_ptp.o fsl-dpa-objs := dpaa_ethtool.o dpaa_eth_sysfs.o \ dpaa_eth.o dpaa_eth_sg.o diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpa/dpaa_eth.h index e26cb72..24504c0 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth.h @@ -616,6 +616,15 @@ static inline void _dpa_assign_wq(struct dpa_fq *fq) skb_get_queue_mapping(skb) #endif +#ifdef CONFIG_PTP_1588_CLOCK_DPAA +struct ptp_priv_s { + struct device_node *node; + struct platform_device *of_dev; + struct mac_device *mac_dev; +}; +extern struct ptp_priv_s ptp_priv; +#endif + static inline void _dpa_bp_free_pf(void *addr) { put_page(virt_to_head_page(addr)); diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth_common.c b/drivers/net/ethernet/freescale/dpa/dpaa_eth_common.c index 8bd25d5..32684d2 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth_common.c +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth_common.c @@ -59,6 +59,10 @@ /* Size in bytes of the FQ taildrop threshold */ #define DPA_FQ_TD 0x200000 +#ifdef CONFIG_PTP_1588_CLOCK_DPAA +struct ptp_priv_s ptp_priv; +#endif + static struct dpa_bp *dpa_bp_array[64]; int dpa_max_frm; @@ -584,6 +588,24 @@ dpa_mac_probe(struct platform_device *_of_dev) } #endif +#ifdef CONFIG_PTP_1588_CLOCK_DPAA + phandle_prop = of_get_property(mac_node, "ptimer-handle", &lenp); + if (phandle_prop && ((mac_dev->phy_if != PHY_INTERFACE_MODE_SGMII) || + ((mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) && + (mac_dev->speed == SPEED_1000)))) { + ptp_priv.node = of_find_node_by_phandle(*phandle_prop); + if (ptp_priv.node) { + ptp_priv.of_dev = of_find_device_by_node(ptp_priv.node); + if (unlikely(ptp_priv.of_dev == NULL)) { + dev_err(dpa_dev, + "Cannot find device represented by timer_node\n"); + of_node_put(ptp_priv.node); + return ERR_PTR(-EINVAL); + } + ptp_priv.mac_dev = mac_dev; + } + } +#endif return mac_dev; } diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_ptp.c b/drivers/net/ethernet/freescale/dpa/dpaa_ptp.c new file mode 100644 index 0000000..34725f8 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpa/dpaa_ptp.c @@ -0,0 +1,288 @@ +/* drivers/net/ethernet/freescale/dpa/dpaa_ptp.c + * + * DPAA Ethernet Driver -- PTP 1588 clock using the dTSEC + * + * Author: Yangbo Lu + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dpaa_eth.h" +#include "mac.h" + +struct ptp_clock *clock; + +static struct mac_device *mac_dev; +static u32 freqCompensation; + +/* Bit definitions for the TMR_CTRL register */ +#define ALM1P (1<<31) /* Alarm1 output polarity */ +#define ALM2P (1<<30) /* Alarm2 output polarity */ +#define FS (1<<28) /* FIPER start indication */ +#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */ +#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */ +#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */ +#define TCLK_PERIOD_MASK (0x3ff) +#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */ +#define FRD (1<<14) /* FIPER Realignment Disable */ +#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */ +#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */ +#define ETEP2 (1<<9) /* External trigger 2 edge polarity */ +#define ETEP1 (1<<8) /* External trigger 1 edge polarity */ +#define COPH (1<<7) /* Generated clock output phase. */ +#define CIPH (1<<6) /* External oscillator input clock phase */ +#define TMSR (1<<5) /* Timer soft reset. */ +#define BYP (1<<3) /* Bypass drift compensated clock */ +#define TE (1<<2) /* 1588 timer enable. */ +#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source */ +#define CKSEL_MASK (0x3) + +/* Bit definitions for the TMR_TEVENT register */ +#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */ +#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */ +#define ALM2 (1<<17) /* Current time = alarm time register 2 */ +#define ALM1 (1<<16) /* Current time = alarm time register 1 */ +#define PP1 (1<<7) /* periodic pulse generated on FIPER1 */ +#define PP2 (1<<6) /* periodic pulse generated on FIPER2 */ +#define PP3 (1<<5) /* periodic pulse generated on FIPER3 */ + +/* Bit definitions for the TMR_TEMASK register */ +#define ETS2EN (1<<25) /* External trigger 2 timestamp enable */ +#define ETS1EN (1<<24) /* External trigger 1 timestamp enable */ +#define ALM2EN (1<<17) /* Timer ALM2 event enable */ +#define ALM1EN (1<<16) /* Timer ALM1 event enable */ +#define PP1EN (1<<7) /* Periodic pulse event 1 enable */ +#define PP2EN (1<<6) /* Periodic pulse event 2 enable */ + +/* Bit definitions for the TMR_PEVENT register */ +#define TXP2 (1<<9) /* PTP transmitted timestamp im TXTS2 */ +#define TXP1 (1<<8) /* PTP transmitted timestamp in TXTS1 */ +#define RXP (1<<0) /* PTP frame has been received */ + +/* Bit definitions for the TMR_PEMASK register */ +#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */ +#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */ +#define RXPEN (1<<0) /* Receive PTP packet event enable */ + +/* Bit definitions for the TMR_STAT register */ +#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */ +#define STAT_VEC_MASK (0x3f) + +/* Bit definitions for the TMR_PRSC register */ +#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */ +#define PRSC_OCK_MASK (0xffff) + + +#define N_EXT_TS 2 + +static void set_alarm(void) +{ + u64 ns; + + if (mac_dev->fm_rtc_get_cnt) + mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns); + ns += 1500000000ULL; + ns = div_u64(ns, 1000000000UL) * 1000000000ULL; + ns -= DPA_PTP_NOMINAL_FREQ_PERIOD_NS; + if (mac_dev->fm_rtc_set_alarm) + mac_dev->fm_rtc_set_alarm(mac_dev->fm_dev, 0, ns); +} + +static void set_fipers(void) +{ + u64 fiper; + + if (mac_dev->fm_rtc_disable) + mac_dev->fm_rtc_disable(mac_dev->fm_dev); + + set_alarm(); + fiper = 1000000000ULL - DPA_PTP_NOMINAL_FREQ_PERIOD_NS; + if (mac_dev->fm_rtc_set_fiper) + mac_dev->fm_rtc_set_fiper(mac_dev->fm_dev, 0, fiper); + + if (mac_dev->fm_rtc_enable) + mac_dev->fm_rtc_enable(mac_dev->fm_dev); +} + +/* PTP clock operations */ + +static int ptp_dpa_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +{ + u64 adj; + u32 diff, tmr_add; + int neg_adj = 0; + + if (ppb < 0) { + neg_adj = 1; + ppb = -ppb; + } + + tmr_add = freqCompensation; + adj = tmr_add; + adj *= ppb; + diff = div_u64(adj, 1000000000ULL); + + tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; + + if (mac_dev->fm_rtc_set_drift) + mac_dev->fm_rtc_set_drift(mac_dev->fm_dev, tmr_add); + + return 0; +} + +static int ptp_dpa_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + s64 now; + + if (mac_dev->fm_rtc_get_cnt) + mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &now); + + now += delta; + + if (mac_dev->fm_rtc_set_cnt) + mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, now); + set_fipers(); + + return 0; +} + +static int ptp_dpa_gettime(struct ptp_clock_info *ptp, struct timespec *ts) +{ + u64 ns; + u32 remainder; + + if (mac_dev->fm_rtc_get_cnt) + mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns); + + ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); + ts->tv_nsec = remainder; + return 0; +} + +static int ptp_dpa_settime(struct ptp_clock_info *ptp, + const struct timespec *ts) +{ + u64 ns; + + ns = ts->tv_sec * 1000000000ULL; + ns += ts->tv_nsec; + + if (mac_dev->fm_rtc_set_cnt) + mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, ns); + set_fipers(); + return 0; +} + +static int ptp_dpa_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + u32 bit; + + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + switch (rq->extts.index) { + case 0: + bit = ETS1EN; + break; + case 1: + bit = ETS2EN; + break; + default: + return -EINVAL; + } + if (on) { + if (mac_dev->fm_rtc_enable_interrupt) + mac_dev->fm_rtc_enable_interrupt( + mac_dev->fm_dev, bit); + } else { + if (mac_dev->fm_rtc_disable_interrupt) + mac_dev->fm_rtc_disable_interrupt( + mac_dev->fm_dev, bit); + } + return 0; + + case PTP_CLK_REQ_PPS: + if (on) { + if (mac_dev->fm_rtc_enable_interrupt) + mac_dev->fm_rtc_enable_interrupt( + mac_dev->fm_dev, PP1EN); + } else { + if (mac_dev->fm_rtc_disable_interrupt) + mac_dev->fm_rtc_disable_interrupt( + mac_dev->fm_dev, PP1EN); + } + return 0; + + default: + break; + } + + return -EOPNOTSUPP; +} + +static struct ptp_clock_info ptp_dpa_caps = { + .owner = THIS_MODULE, + .name = "dpaa clock", + .max_adj = 512000, + .n_alarm = 0, + .n_ext_ts = N_EXT_TS, + .n_per_out = 0, + .pps = 1, + .adjfreq = ptp_dpa_adjfreq, + .adjtime = ptp_dpa_adjtime, + .gettime = ptp_dpa_gettime, + .settime = ptp_dpa_settime, + .enable = ptp_dpa_enable, +}; + +static int __init __cold dpa_ptp_load(void) +{ + struct device *ptp_dev; + struct timespec now; + int dpa_phc_index; + int err; + + ptp_dev = &ptp_priv.of_dev->dev; + mac_dev = ptp_priv.mac_dev; + + if (mac_dev->fm_rtc_get_drift) + mac_dev->fm_rtc_get_drift(mac_dev->fm_dev, &freqCompensation); + + getnstimeofday(&now); + ptp_dpa_settime(&ptp_dpa_caps, &now); + + clock = ptp_clock_register(&ptp_dpa_caps, ptp_dev); + if (IS_ERR(clock)) { + err = PTR_ERR(clock); + return err; + } + dpa_phc_index = ptp_clock_index(clock); + return 0; +} +module_init(dpa_ptp_load); + +static void __exit __cold dpa_ptp_unload(void) +{ + if (mac_dev->fm_rtc_disable_interrupt) + mac_dev->fm_rtc_disable_interrupt(mac_dev->fm_dev, 0xffffffff); + ptp_clock_unregister(clock); +} +module_exit(dpa_ptp_unload); diff --git a/drivers/net/ethernet/freescale/dpa/mac.h b/drivers/net/ethernet/freescale/dpa/mac.h index f9e2db2..54a5bbf 100644 --- a/drivers/net/ethernet/freescale/dpa/mac.h +++ b/drivers/net/ethernet/freescale/dpa/mac.h @@ -96,6 +96,10 @@ struct mac_device { int (*fm_rtc_set_alarm)(struct fm *fm_dev, uint32_t id, uint64_t time); int (*fm_rtc_set_fiper)(struct fm *fm_dev, uint32_t id, uint64_t fiper); +#ifdef CONFIG_PTP_1588_CLOCK_DPAA + int (*fm_rtc_enable_interrupt)(struct fm *fm_dev, uint32_t events); + int (*fm_rtc_disable_interrupt)(struct fm *fm_dev, uint32_t events); +#endif int (*set_wol)(struct fm_port *port, struct fm_mac_dev *fm_mac_dev, bool en); int (*dump_mac_regs)(struct mac_device *h_mac, char *buf, int nn); diff --git a/drivers/net/ethernet/freescale/fman/Peripherals/FM/Rtc/fm_rtc.c b/drivers/net/ethernet/freescale/fman/Peripherals/FM/Rtc/fm_rtc.c index 312e5e3..2745dc4 100644 --- a/drivers/net/ethernet/freescale/fman/Peripherals/FM/Rtc/fm_rtc.c +++ b/drivers/net/ethernet/freescale/fman/Peripherals/FM/Rtc/fm_rtc.c @@ -661,6 +661,36 @@ t_Error FM_RTC_SetFreqCompensation(t_Handle h_FmRtc, uint32_t freqCompensation) return E_OK; } +#ifdef CONFIG_PTP_1588_CLOCK_DPAA +/*****************************************************************************/ +t_Error FM_RTC_EnableInterrupt(t_Handle h_FmRtc, uint32_t events) +{ + t_FmRtc *p_Rtc = (t_FmRtc *)h_FmRtc; + + SANITY_CHECK_RETURN_ERROR(p_Rtc, E_INVALID_HANDLE); + SANITY_CHECK_RETURN_ERROR(!p_Rtc->p_RtcDriverParam, E_INVALID_STATE); + + /* enable interrupt */ + fman_rtc_enable_interupt(p_Rtc->p_MemMap, events); + + return E_OK; +} + +/*****************************************************************************/ +t_Error FM_RTC_DisableInterrupt(t_Handle h_FmRtc, uint32_t events) +{ + t_FmRtc *p_Rtc = (t_FmRtc *)h_FmRtc; + + SANITY_CHECK_RETURN_ERROR(p_Rtc, E_INVALID_HANDLE); + SANITY_CHECK_RETURN_ERROR(!p_Rtc->p_RtcDriverParam, E_INVALID_STATE); + + /* disable interrupt */ + fman_rtc_disable_interupt(p_Rtc->p_MemMap, events); + + return E_OK; +} +#endif + /*****************************************************************************/ #if (defined(DEBUG_ERRORS) && (DEBUG_ERRORS > 0)) t_Error FM_RTC_DumpRegs(t_Handle h_FmRtc) diff --git a/drivers/net/ethernet/freescale/fman/inc/Peripherals/fm_rtc_ext.h b/drivers/net/ethernet/freescale/fman/inc/Peripherals/fm_rtc_ext.h index 315405c..72078ac 100644 --- a/drivers/net/ethernet/freescale/fman/inc/Peripherals/fm_rtc_ext.h +++ b/drivers/net/ethernet/freescale/fman/inc/Peripherals/fm_rtc_ext.h @@ -570,6 +570,32 @@ t_Error FM_RTC_GetFreqCompensation(t_Handle h_FmRtc, uint32_t *p_Compensation); *//***************************************************************************/ t_Error FM_RTC_SetFreqCompensation(t_Handle h_FmRtc, uint32_t freqCompensation); +#ifdef CONFIG_PTP_1588_CLOCK_DPAA +/**************************************************************************//** +*@Function FM_RTC_EnableInterrupt +* +*@Description Enable interrupt of FM RTC. +* +*@Param[in] h_FmRtc - Handle to FM RTC object. +*@Param[in] events - Interrupt events. +* +*@Return E_OK on success; Error code otherwise. +*//***************************************************************************/ +t_Error FM_RTC_EnableInterrupt(t_Handle h_FmRtc, uint32_t events); + +/**************************************************************************//** +*@Function FM_RTC_DisableInterrupt +* +*@Description Disable interrupt of FM RTC. +* +*@Param[in] h_FmRtc - Handle to FM RTC object. +*@Param[in] events - Interrupt events. +* +*@Return E_OK on success; Error code otherwise. +*//***************************************************************************/ +t_Error FM_RTC_DisableInterrupt(t_Handle h_FmRtc, uint32_t events); +#endif + #if (defined(DEBUG_ERRORS) && (DEBUG_ERRORS > 0)) /**************************************************************************//** @Function FM_RTC_DumpRegs diff --git a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c index 74b0de8..58283fd 100755 --- a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c +++ b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c @@ -1942,6 +1942,38 @@ int fm_rtc_set_fiper(struct fm *fm_dev, uint32_t id, } EXPORT_SYMBOL(fm_rtc_set_fiper); +#ifdef CONFIG_PTP_1588_CLOCK_DPAA +int fm_rtc_enable_interrupt(struct fm *fm_dev, uint32_t events) +{ + int _errno; + t_Error err; + + err = FM_RTC_EnableInterrupt(fm_get_rtc_handle(fm_dev), + events); + _errno = -GET_ERROR_TYPE(err); + if (unlikely(_errno < 0)) + pr_err("FM_RTC_EnableInterrupt = 0x%08x\n", err); + + return _errno; +} +EXPORT_SYMBOL(fm_rtc_enable_interrupt); + +int fm_rtc_disable_interrupt(struct fm *fm_dev, uint32_t events) +{ + int _errno; + t_Error err; + + err = FM_RTC_DisableInterrupt(fm_get_rtc_handle(fm_dev), + events); + _errno = -GET_ERROR_TYPE(err); + if (unlikely(_errno < 0)) + pr_err("FM_RTC_DisableInterrupt = 0x%08x\n", err); + + return _errno; +} +EXPORT_SYMBOL(fm_rtc_disable_interrupt); +#endif + int fm_mac_set_wol(struct fm_port *port, struct fm_mac_dev *fm_mac_dev, bool en) { int _errno; diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index 5be73ba..eb53628 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -37,6 +37,21 @@ config PTP_1588_CLOCK_GIANFAR To compile this driver as a module, choose M here: the module will be called gianfar_ptp. +config PTP_1588_CLOCK_DPAA + tristate "Freescale dTSEC as PTP clock" + depends on FSL_DPAA_ETH + select PTP_1588_CLOCK + select FSL_DPAA_TS + default n + help + This driver adds support for using the dTSEC as a PTP + clock. This clock is only useful if your PTP programs are + getting hardware time stamps on the PTP Ethernet packets + using the SO_TIMESTAMPING API. + + To compile this driver as a module, choose M here: the module + will be called dpaa_ptp. + config PTP_1588_CLOCK_IXP46X tristate "Intel IXP46x as PTP clock" depends on IXP4XX_ETH -- cgit v0.10.2