* [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support.
@ 2014-09-28 4:20 Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 1/4] net: fec: ptp: Use the 31-bit ptp timer Luwei Zhou
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Luwei Zhou @ 2014-09-28 4:20 UTC (permalink / raw)
To: davem, richardcochran
Cc: netdev, shawn.guo, bhutchings, R49496, b38611, b20596, stephen
These patches use hardeware adjustment to ptp timer and add pps ouput
support.
Change from v1 to v2:
- Fix the potential 32-bit multiplication overflow issue.
- Optimize the hareware adjustment code to improve efficiency as Richard suggested
- Use ptp PTP_PIN_SET/GETFUNC interface to set PPS channel not device tree
and add PTP_PF_PPS enumeration
- Modify comments style
Luwei Zhou (4):
net: fec: ptp: Use the 31-bit ptp timer.
net: fec: ptp: Use hardware algorithm to adjust PTP counter.
ptp: Add PTP_PF_PPS enumeration to ptp_pin_function.
net: fec: ptp: Enalbe PPS ouput based on ptp clock
drivers/net/ethernet/freescale/fec.h | 10 +
drivers/net/ethernet/freescale/fec_main.c | 2 +
drivers/net/ethernet/freescale/fec_ptp.c | 293 ++++++++++++++++++++++++++++--
drivers/ptp/ptp_chardev.c | 8 +
include/linux/ptp_clock_kernel.h | 2 +
include/uapi/linux/ptp_clock.h | 1 +
6 files changed, 298 insertions(+), 18 deletions(-)
--
1.9.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/4] net: fec: ptp: Use the 31-bit ptp timer.
2014-09-28 4:20 [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support Luwei Zhou
@ 2014-09-28 4:20 ` Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter Luwei Zhou
` (2 subsequent siblings)
3 siblings, 0 replies; 9+ messages in thread
From: Luwei Zhou @ 2014-09-28 4:20 UTC (permalink / raw)
To: davem, richardcochran
Cc: netdev, shawn.guo, bhutchings, R49496, b38611, b20596, stephen
When ptp switches from software adjustment to hardware ajustment, linux ptp can't converge.
It is caused by the IP limit. Hardware adjustment logcial have issue when ptp counter
runs over 0x80000000(31 bit counter). The internal IP reference manual already remove 32bit
free-running count support. This patch replace the 32-bit PTP timer with 31-bit.
Signed-off-by: Luwei Zhou <b45643@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec_ptp.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index cca3617..8016bdd 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -70,6 +70,7 @@
#define FEC_TS_TIMESTAMP 0x418
#define FEC_CC_MULT (1 << 31)
+#define FEC_COUNTER_PERIOD (1 << 31)
/**
* fec_ptp_read - read raw cycle counter (to be used by time counter)
* @cc: the cyclecounter structure
@@ -113,14 +114,15 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev)
/* 1ns counter */
writel(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC);
- /* use free running count */
- writel(0, fep->hwp + FEC_ATIME_EVT_PERIOD);
+ /* use 31-bit timer counter */
+ writel(FEC_COUNTER_PERIOD, fep->hwp + FEC_ATIME_EVT_PERIOD);
- writel(FEC_T_CTRL_ENABLE, fep->hwp + FEC_ATIME_CTRL);
+ writel(FEC_T_CTRL_ENABLE | FEC_T_CTRL_PERIOD_RST,
+ fep->hwp + FEC_ATIME_CTRL);
memset(&fep->cc, 0, sizeof(fep->cc));
fep->cc.read = fec_ptp_read;
- fep->cc.mask = CLOCKSOURCE_MASK(32);
+ fep->cc.mask = CLOCKSOURCE_MASK(31);
fep->cc.shift = 31;
fep->cc.mult = FEC_CC_MULT;
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter.
2014-09-28 4:20 [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 1/4] net: fec: ptp: Use the 31-bit ptp timer Luwei Zhou
@ 2014-09-28 4:20 ` Luwei Zhou
2014-09-29 2:13 ` fugang.duan
2014-09-28 4:20 ` [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 4/4] net: fec: ptp: Enalbe PPS ouput based on ptp clock Luwei Zhou
3 siblings, 1 reply; 9+ messages in thread
From: Luwei Zhou @ 2014-09-28 4:20 UTC (permalink / raw)
To: davem, richardcochran
Cc: netdev, shawn.guo, bhutchings, R49496, b38611, b20596, stephen
The FEC IP supports hardware adjustment for ptp timer. Refer to the description of
ENET_ATCOR and ENET_ATINC registers in the spec about the hardware adjustment. This
patch uses hardware support to adjust the ptp offset and frequency on the slave side.
Signed-off-by: Luwei Zhou <b45643@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Fugang Duan <b38611@freescale.com>
---
drivers/net/ethernet/freescale/fec.h | 3 ++
drivers/net/ethernet/freescale/fec_ptp.c | 66 ++++++++++++++++++++++++++------
2 files changed, 57 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 354a309..b2e263e 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -482,6 +482,9 @@ struct fec_enet_private {
unsigned int tx_pkts_itr;
unsigned int tx_time_itr;
unsigned int itr_clk_rate;
+
+ /* ptp clock period in ns*/
+ unsigned int ptp_inc;
};
void fec_ptp_init(struct platform_device *pdev);
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 8016bdd..036d956 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -71,6 +71,7 @@
#define FEC_CC_MULT (1 << 31)
#define FEC_COUNTER_PERIOD (1 << 31)
+#define FEC_T_NSEC_PER_SEC (1000000000UL)
/**
* fec_ptp_read - read raw cycle counter (to be used by time counter)
* @cc: the cyclecounter structure
@@ -145,32 +146,59 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev)
*/
static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{
- u64 diff;
unsigned long flags;
int neg_adj = 0;
- u32 mult = FEC_CC_MULT;
+ u32 i, tmp;
+ u32 corr_inc, corr_period;
+ u32 corr_ns;
+ u64 lhs, rhs;
struct fec_enet_private *fep =
container_of(ptp, struct fec_enet_private, ptp_caps);
+ if (ppb == 0)
+ return 0;
+
if (ppb < 0) {
ppb = -ppb;
neg_adj = 1;
}
- diff = mult;
- diff *= ppb;
- diff = div_u64(diff, 1000000000ULL);
+ /* In theory, corr_inc/corr_period = ppb/FEC_T_NSEC_PER_SEC;
+ * Try to find the corr_inc between 1 to fep->ptp_inc to
+ * meet adjustment requirement.
+ */
+ lhs = FEC_T_NSEC_PER_SEC;
+ rhs = (u64)ppb * (u64)fep->ptp_inc;
+ for (i = 1; i <= fep->ptp_inc; i++) {
+ if (lhs >= rhs) {
+ corr_inc = i;
+ corr_period = div_u64(lhs, rhs);
+ break;
+ }
+ lhs += FEC_T_NSEC_PER_SEC;
+ }
+ /* Not found? Set it to high value - double speed
+ * correct in every clock step.
+ */
+ if (i > fep->ptp_inc) {
+ corr_inc = fep->ptp_inc;
+ corr_period = 1;
+ }
+
+ if (neg_adj)
+ corr_ns = fep->ptp_inc - corr_inc;
+ else
+ corr_ns = fep->ptp_inc + corr_inc;
spin_lock_irqsave(&fep->tmreg_lock, flags);
- /*
- * dummy read to set cycle_last in tc to now.
- * So use adjusted mult to calculate when next call
- * timercounter_read.
- */
- timecounter_read(&fep->tc);
- fep->cc.mult = neg_adj ? mult - diff : mult + diff;
+ tmp = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK;
+ tmp |= corr_ns << FEC_T_INC_CORR_OFFSET;
+ writel(tmp, fep->hwp + FEC_ATIME_INC);
+ writel(corr_period, fep->hwp + FEC_ATIME_CORR);
+ /* dummy read to update the timer. */
+ timecounter_read(&fep->tc);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
@@ -190,12 +218,19 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
container_of(ptp, struct fec_enet_private, ptp_caps);
unsigned long flags;
u64 now;
+ u32 counter;
spin_lock_irqsave(&fep->tmreg_lock, flags);
now = timecounter_read(&fep->tc);
now += delta;
+ /* Get the timer value based on adjusted timestamp.
+ * Update the counter with the masked value.
+ */
+ counter = now & fep->cc.mask;
+ writel(counter, fep->hwp + FEC_ATIME);
+
/* reset the timecounter */
timecounter_init(&fep->tc, &fep->cc, now);
@@ -246,6 +281,7 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
u64 ns;
unsigned long flags;
+ u32 counter;
mutex_lock(&fep->ptp_clk_mutex);
/* Check the ptp clock */
@@ -256,8 +292,13 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
ns = ts->tv_sec * 1000000000ULL;
ns += ts->tv_nsec;
+ /* Get the timer value based on timestamp.
+ * Update the counter with the masked value.
+ */
+ counter = ns & fep->cc.mask;
spin_lock_irqsave(&fep->tmreg_lock, flags);
+ writel(counter, fep->hwp + FEC_ATIME);
timecounter_init(&fep->tc, &fep->cc, ns);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
mutex_unlock(&fep->ptp_clk_mutex);
@@ -396,6 +437,7 @@ void fec_ptp_init(struct platform_device *pdev)
fep->ptp_caps.enable = fec_ptp_enable;
fep->cycle_speed = clk_get_rate(fep->clk_ptp);
+ fep->ptp_inc = FEC_T_NSEC_PER_SEC / fep->cycle_speed;
spin_lock_init(&fep->tmreg_lock);
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function.
2014-09-28 4:20 [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 1/4] net: fec: ptp: Use the 31-bit ptp timer Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter Luwei Zhou
@ 2014-09-28 4:20 ` Luwei Zhou
2014-09-29 8:56 ` Richard Cochran
2014-09-28 4:20 ` [PATCH v2 4/4] net: fec: ptp: Enalbe PPS ouput based on ptp clock Luwei Zhou
3 siblings, 1 reply; 9+ messages in thread
From: Luwei Zhou @ 2014-09-28 4:20 UTC (permalink / raw)
To: davem, richardcochran
Cc: netdev, shawn.guo, bhutchings, R49496, b38611, b20596, stephen
PTP_PIN_SET/GETFUNC interface can be used to specify the channel index
when multi-channel timer is used to ouput PPS. Need to add PPS corresponding
pin function enumeration in the ptp.
Signed-off-by: Luwei Zhou <b45643@freescale.com>
---
drivers/ptp/ptp_chardev.c | 8 ++++++++
include/linux/ptp_clock_kernel.h | 2 ++
include/uapi/linux/ptp_clock.h | 1 +
3 files changed, 11 insertions(+)
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index f8a7609..a746e5a 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -48,6 +48,10 @@ static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
break;
case PTP_PF_PHYSYNC:
break;
+ case PTP_PF_PPS:
+ rq.type = PTP_CLK_REQ_PPS;
+ err = ops->enable(ops, &rq, 0);
+ break;
default:
return -EINVAL;
}
@@ -85,6 +89,10 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
if (chan >= info->n_per_out)
return -EINVAL;
break;
+ case PTP_PF_PPS:
+ if (chan >= info->n_pps_chan)
+ return -EINVAL;
+ break;
case PTP_PF_PHYSYNC:
if (chan != 0)
return -EINVAL;
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 0d8ff3f..2a5ad1c 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -49,6 +49,7 @@ struct ptp_clock_request {
* @n_alarm: The number of programmable alarms.
* @n_ext_ts: The number of external time stamp channels.
* @n_per_out: The number of programmable periodic signals.
+ * @n_pps_chan: The number of programmable channles for pps output.
* @n_pins: The number of programmable pins.
* @pps: Indicates whether the clock supports a PPS callback.
* @pin_config: Array of length 'n_pins'. If the number of
@@ -99,6 +100,7 @@ struct ptp_clock_info {
int n_alarm;
int n_ext_ts;
int n_per_out;
+ int n_pps_chan;
int n_pins;
int pps;
struct ptp_pin_desc *pin_config;
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index f0b7bfe..57ba7e7 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -86,6 +86,7 @@ enum ptp_pin_function {
PTP_PF_EXTTS,
PTP_PF_PEROUT,
PTP_PF_PHYSYNC,
+ PTP_PF_PPS,
};
struct ptp_pin_desc {
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 4/4] net: fec: ptp: Enalbe PPS ouput based on ptp clock
2014-09-28 4:20 [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support Luwei Zhou
` (2 preceding siblings ...)
2014-09-28 4:20 ` [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function Luwei Zhou
@ 2014-09-28 4:20 ` Luwei Zhou
3 siblings, 0 replies; 9+ messages in thread
From: Luwei Zhou @ 2014-09-28 4:20 UTC (permalink / raw)
To: davem, richardcochran
Cc: netdev, shawn.guo, bhutchings, R49496, b38611, b20596, stephen
FEC ptp timer has 4 channel compare/trigger function. It can be used to
enable pps output.
The pulse would be ouput high exactly on N second. The pulse ouput high
on compare event mode is used to produce pulse per second. The pulse
width would be one cycle based on ptp timer clock source.Since 31-bit
ptp hardware timer is used, the timer will wrap more than 2 seconds. We
need to reload the compare compare event about every 1 second.
Signed-off-by: Luwei Zhou <b45643@freescale.com>
---
drivers/net/ethernet/freescale/fec.h | 7 +
drivers/net/ethernet/freescale/fec_main.c | 2 +
drivers/net/ethernet/freescale/fec_ptp.c | 217 +++++++++++++++++++++++++++++-
3 files changed, 224 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index b2e263e..1dbc014 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -485,12 +485,19 @@ struct fec_enet_private {
/* ptp clock period in ns*/
unsigned int ptp_inc;
+
+ /* pps */
+ int pps_channel;
+ unsigned int reload_period;
+ int pps_enable;
+ unsigned int next_counter;
};
void fec_ptp_init(struct platform_device *pdev);
void fec_ptp_start_cyclecounter(struct net_device *ndev);
int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr);
int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr);
+uint fec_ptp_check_pps_event(struct fec_enet_private *fep);
/****************************************************************************/
#endif /* FEC_H */
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 2c73434..5342f45 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1571,6 +1571,8 @@ fec_enet_interrupt(int irq, void *dev_id)
complete(&fep->mdio_done);
}
+ fec_ptp_check_pps_event(fep);
+
return ret;
}
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 036d956..4ac2ff7 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -61,6 +61,24 @@
#define FEC_T_INC_CORR_MASK 0x00007f00
#define FEC_T_INC_CORR_OFFSET 8
+#define FEC_T_CTRL_PINPER 0x00000080
+#define FEC_T_TF0_MASK 0x00000001
+#define FEC_T_TF0_OFFSET 0
+#define FEC_T_TF1_MASK 0x00000002
+#define FEC_T_TF1_OFFSET 1
+#define FEC_T_TF2_MASK 0x00000004
+#define FEC_T_TF2_OFFSET 2
+#define FEC_T_TF3_MASK 0x00000008
+#define FEC_T_TF3_OFFSET 3
+#define FEC_T_TDRE_MASK 0x00000001
+#define FEC_T_TDRE_OFFSET 0
+#define FEC_T_TMODE_MASK 0x0000003C
+#define FEC_T_TMODE_OFFSET 2
+#define FEC_T_TIE_MASK 0x00000040
+#define FEC_T_TIE_OFFSET 6
+#define FEC_T_TF_MASK 0x00000080
+#define FEC_T_TF_OFFSET 7
+
#define FEC_ATIME_CTRL 0x400
#define FEC_ATIME 0x404
#define FEC_ATIME_EVT_OFFSET 0x408
@@ -69,9 +87,160 @@
#define FEC_ATIME_INC 0x414
#define FEC_TS_TIMESTAMP 0x418
+#define FEC_TGSR 0x604
+#define FEC_TCSR(n) (0x608 + n * 0x08)
+#define FEC_TCCR(n) (0x60C + n * 0x08)
+#define MAX_TIMER_CHANNEL 3
+#define FEC_TMODE_TOGGLE 0x05
+#define FEC_HIGH_PULSE 0x0F
+
#define FEC_CC_MULT (1 << 31)
#define FEC_COUNTER_PERIOD (1 << 31)
#define FEC_T_NSEC_PER_SEC (1000000000UL)
+#define PPS_OUPUT_RELOAD_PERIOD FEC_T_NSEC_PER_SEC
+
+/**
+ * The default PPS ouput pin configuration.
+ * User can set/get the PPS output channel
+ * via PTP_PIN_SET/GETFUNC ptp interface.
+ */
+static struct ptp_pin_desc fec_pps_pin = {
+ .name = "fec_pps_pin_config",
+ .index = 0,
+ .func = PTP_PF_PPS,
+ .chan = 0,
+};
+
+/**
+ * fec_ptp_enable_pps
+ * @fep: the fec_enet_private structure handle
+ * @enable: enable the channel pps output
+ *
+ * This function enble the PPS ouput on the timer channel.
+ */
+static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
+{
+ unsigned long flags;
+ u32 val, tempval;
+ int inc;
+ struct timespec ts;
+ u64 ns;
+ u32 remainder;
+ struct ptp_pin_desc *pin_config = fep->ptp_caps.pin_config;
+ val = 0;
+
+ if (pin_config->func != PTP_PF_PPS) {
+ dev_err(&fep->pdev->dev, "No pps pin function\n");
+ return -EINVAL;
+ }
+
+ if (!(fep->hwts_tx_en || fep->hwts_rx_en)) {
+ dev_err(&fep->pdev->dev, "No ptp stack is running\n");
+ return -EINVAL;
+ }
+
+ if (fep->pps_enable == enable)
+ return 0;
+
+ fep->pps_channel = pin_config->chan;
+ fep->reload_period = PPS_OUPUT_RELOAD_PERIOD;
+ inc = fep->ptp_inc;
+
+ spin_lock_irqsave(&fep->tmreg_lock, flags);
+
+ if (enable) {
+ /* clear capture or output compare interrupt status if have.
+ */
+ writel(FEC_T_TF_MASK, fep->hwp + FEC_TCSR(fep->pps_channel));
+
+ /* It is recommended to doulbe check the TMODE field in the
+ * TCSR register to be cleared before the first compare counter
+ * is written into TCCR register. Just add a double check.
+ */
+ val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
+ do {
+ val &= ~(FEC_T_TMODE_MASK);
+ writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
+ val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
+ } while (val & FEC_T_TMODE_MASK);
+
+ /* Dummy read counter to update the counter */
+ timecounter_read(&fep->tc);
+ /* We want to find the first compare event in the next
+ * second point. So we need to know what the ptp time
+ * is now and how many nanoseconds is ahead to get next second.
+ * The remaining nanosecond ahead before the next second would be
+ * FEC_T_NSEC_PER_SEC - ts.tv_nsec. Add the remaining nanoseconds
+ * to current timer would be next second.
+ */
+ tempval = readl(fep->hwp + FEC_ATIME_CTRL);
+ tempval |= FEC_T_CTRL_CAPTURE;
+ writel(tempval, fep->hwp + FEC_ATIME_CTRL);
+
+ tempval = readl(fep->hwp + FEC_ATIME);
+ /* Convert the ptp local counter to 1588 timestamp */
+ ns = timecounter_cyc2time(&fep->tc, tempval);
+ ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder);
+ ts.tv_nsec = remainder;
+
+ /* The tempval is less than 3 seconds, and so val is less than
+ * 4 seconds. No overflow for 32bit calculation.
+ */
+ val = FEC_T_NSEC_PER_SEC - (u32)ts.tv_nsec + tempval;
+
+ /* Need to consider the situation that the current time is
+ * very close to the second point, which means FEC_T_NSEC_PER_SEC
+ * - ts.tv_nsec is close to be zero(For example 20ns); Since the timer
+ * is still running when we calculate the first compare event, it is
+ * possible that the remaining nanoseonds run out before the compare
+ * counter is calculated and written into TCCR register. To avoid
+ * this possibility, we will set the compare event to be the next
+ * of next second. The current setting is 31-bit timer and wrap
+ * around over 2 seconds. So it is okay to set the next of next
+ * seond for the timer.
+ */
+ val += FEC_T_NSEC_PER_SEC;
+
+ /* We add (2 *FEC_T_NSEC_PER_SEC - (u32)ts.tv_nsec) to current
+ * ptp counter, which maybe cause 32-bit wrap. Since the
+ * (FEC_T_NSEC_PER_SEC - (u32)ts.tv_nsec) is less than 2 second.
+ * We can ensure the wrap will not cause issue. If the offset
+ * is bigger than fep->cc.mask would be a error.
+ */
+ val &= fep->cc.mask;
+ writel(val, fep->hwp + FEC_TCCR(fep->pps_channel));
+
+ /* Calculate the second the compare event timestamp */
+ fep->next_counter = (val + fep->reload_period) & fep->cc.mask;
+
+ /* * Enable compare event when overflow */
+ val = readl(fep->hwp + FEC_ATIME_CTRL);
+ val |= FEC_T_CTRL_PINPER;
+ writel(val, fep->hwp + FEC_ATIME_CTRL);
+
+ /* Compare channel setting. */
+ val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
+ val |= (1 << FEC_T_TF_OFFSET | 1 << FEC_T_TIE_OFFSET);
+ val &= ~(1 << FEC_T_TDRE_OFFSET);
+ val &= ~(FEC_T_TMODE_MASK);
+ val |= (FEC_HIGH_PULSE << FEC_T_TMODE_OFFSET);
+ writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
+
+ /* Write the second compare event timestamp and calculate
+ * the third timestamp. Refer the TCCR register detail in the spec.
+ */
+ writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel));
+ fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask;
+ } else {
+ writel(0, fep->hwp + FEC_TCSR(fep->pps_channel));
+ }
+
+ fep->pps_enable = enable;
+ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+
+ return 0;
+}
+
/**
* fec_ptp_read - read raw cycle counter (to be used by time counter)
* @cc: the cyclecounter structure
@@ -315,6 +484,15 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
static int fec_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
+ struct fec_enet_private *fep =
+ container_of(ptp, struct fec_enet_private, ptp_caps);
+ int ret = 0;
+
+ if (rq->type == PTP_CLK_REQ_PPS) {
+ ret = fec_ptp_enable_pps(fep, on);
+
+ return ret;
+ }
return -EOPNOTSUPP;
}
@@ -428,8 +606,10 @@ void fec_ptp_init(struct platform_device *pdev)
fep->ptp_caps.n_alarm = 0;
fep->ptp_caps.n_ext_ts = 0;
fep->ptp_caps.n_per_out = 0;
- fep->ptp_caps.n_pins = 0;
- fep->ptp_caps.pps = 0;
+ fep->ptp_caps.n_pps_chan = MAX_TIMER_CHANNEL;
+ fep->ptp_caps.n_pins = 1;
+ fep->ptp_caps.pin_config = &fec_pps_pin;
+ fep->ptp_caps.pps = 1;
fep->ptp_caps.adjfreq = fec_ptp_adjfreq;
fep->ptp_caps.adjtime = fec_ptp_adjtime;
fep->ptp_caps.gettime = fec_ptp_gettime;
@@ -453,3 +633,36 @@ void fec_ptp_init(struct platform_device *pdev)
schedule_delayed_work(&fep->time_keep, HZ);
}
+
+/**
+ * fec_ptp_check_pps_event
+ * @fep: the fec_enet_private structure handle
+ *
+ * This function check the pps event and reload the timer compare counter.
+ */
+uint fec_ptp_check_pps_event(struct fec_enet_private *fep)
+{
+ u32 val;
+ u8 channel = fep->pps_channel;
+ struct ptp_clock_event event;
+
+ val = readl(fep->hwp + FEC_TCSR(channel));
+ if (val & FEC_T_TF_MASK) {
+ /* Write the next next compare(not the next according the spec)
+ * value to the register
+ */
+ writel(fep->next_counter, fep->hwp + FEC_TCCR(channel));
+ do {
+ writel(val, fep->hwp + FEC_TCSR(channel));
+ } while (readl(fep->hwp + FEC_TCSR(channel)) & FEC_T_TF_MASK);
+
+ /* Update the counter; */
+ fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask;
+
+ event.type = PTP_CLOCK_PPS;
+ ptp_clock_event(fep->ptp_clock, &event);
+ return 1;
+ }
+
+ return 0;
+}
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter.
2014-09-28 4:20 ` [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter Luwei Zhou
@ 2014-09-29 2:13 ` fugang.duan
0 siblings, 0 replies; 9+ messages in thread
From: fugang.duan @ 2014-09-29 2:13 UTC (permalink / raw)
To: luwei.zhou@freescale.com, davem@davemloft.net,
richardcochran@gmail.com
Cc: netdev@vger.kernel.org, shawn.guo@linaro.org,
bhutchings@solarflare.com, Fabio.Estevam@freescale.com,
Frank.Li@freescale.com, stephen@networkplumber.org
From: Luwei Zhou <b45643@freescale.com> Sent: Sunday, September 28, 2014 12:20 PM + 0800
>To: davem@davemloft.net; richardcochran@gmail.com
>Cc: netdev@vger.kernel.org; shawn.guo@linaro.org;
>bhutchings@solarflare.com; Estevam Fabio-R49496; Duan Fugang-B38611; Li
>Frank-B20596; stephen@networkplumber.org
>Subject: [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust
>PTP counter.
>
>The FEC IP supports hardware adjustment for ptp timer. Refer to the
>description of ENET_ATCOR and ENET_ATINC registers in the spec about the
>hardware adjustment. This patch uses hardware support to adjust the ptp
>offset and frequency on the slave side.
>
>Signed-off-by: Luwei Zhou <b45643@freescale.com>
>Signed-off-by: Frank Li <Frank.Li@freescale.com>
>Signed-off-by: Fugang Duan <b38611@freescale.com>
>---
> drivers/net/ethernet/freescale/fec.h | 3 ++
> drivers/net/ethernet/freescale/fec_ptp.c | 66 ++++++++++++++++++++++++++-
>-----
> 2 files changed, 57 insertions(+), 12 deletions(-)
>
>diff --git a/drivers/net/ethernet/freescale/fec.h
>b/drivers/net/ethernet/freescale/fec.h
>index 354a309..b2e263e 100644
>--- a/drivers/net/ethernet/freescale/fec.h
>+++ b/drivers/net/ethernet/freescale/fec.h
>@@ -482,6 +482,9 @@ struct fec_enet_private {
> unsigned int tx_pkts_itr;
> unsigned int tx_time_itr;
> unsigned int itr_clk_rate;
>+
>+ /* ptp clock period in ns*/
>+ unsigned int ptp_inc;
> };
>
> void fec_ptp_init(struct platform_device *pdev); diff --git
>a/drivers/net/ethernet/freescale/fec_ptp.c
>b/drivers/net/ethernet/freescale/fec_ptp.c
>index 8016bdd..036d956 100644
>--- a/drivers/net/ethernet/freescale/fec_ptp.c
>+++ b/drivers/net/ethernet/freescale/fec_ptp.c
>@@ -71,6 +71,7 @@
>
> #define FEC_CC_MULT (1 << 31)
> #define FEC_COUNTER_PERIOD (1 << 31)
>+#define FEC_T_NSEC_PER_SEC (1000000000UL)
Don't need redefine it, use NSEC_PER_SEC instead.
> /**
> * fec_ptp_read - read raw cycle counter (to be used by time counter)
> * @cc: the cyclecounter structure
>@@ -145,32 +146,59 @@ void fec_ptp_start_cyclecounter(struct net_device
>*ndev)
> */
> static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) {
>- u64 diff;
> unsigned long flags;
> int neg_adj = 0;
>- u32 mult = FEC_CC_MULT;
>+ u32 i, tmp;
>+ u32 corr_inc, corr_period;
>+ u32 corr_ns;
>+ u64 lhs, rhs;
>
> struct fec_enet_private *fep =
> container_of(ptp, struct fec_enet_private, ptp_caps);
>
>+ if (ppb == 0)
>+ return 0;
>+
> if (ppb < 0) {
> ppb = -ppb;
> neg_adj = 1;
> }
>
>- diff = mult;
>- diff *= ppb;
>- diff = div_u64(diff, 1000000000ULL);
>+ /* In theory, corr_inc/corr_period = ppb/FEC_T_NSEC_PER_SEC;
>+ * Try to find the corr_inc between 1 to fep->ptp_inc to
>+ * meet adjustment requirement.
>+ */
>+ lhs = FEC_T_NSEC_PER_SEC;
>+ rhs = (u64)ppb * (u64)fep->ptp_inc;
>+ for (i = 1; i <= fep->ptp_inc; i++) {
>+ if (lhs >= rhs) {
>+ corr_inc = i;
>+ corr_period = div_u64(lhs, rhs);
>+ break;
>+ }
>+ lhs += FEC_T_NSEC_PER_SEC;
>+ }
>+ /* Not found? Set it to high value - double speed
>+ * correct in every clock step.
>+ */
>+ if (i > fep->ptp_inc) {
>+ corr_inc = fep->ptp_inc;
>+ corr_period = 1;
>+ }
>+
>+ if (neg_adj)
>+ corr_ns = fep->ptp_inc - corr_inc;
>+ else
>+ corr_ns = fep->ptp_inc + corr_inc;
>
> spin_lock_irqsave(&fep->tmreg_lock, flags);
>- /*
>- * dummy read to set cycle_last in tc to now.
>- * So use adjusted mult to calculate when next call
>- * timercounter_read.
>- */
>- timecounter_read(&fep->tc);
>
>- fep->cc.mult = neg_adj ? mult - diff : mult + diff;
>+ tmp = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK;
>+ tmp |= corr_ns << FEC_T_INC_CORR_OFFSET;
>+ writel(tmp, fep->hwp + FEC_ATIME_INC);
>+ writel(corr_period, fep->hwp + FEC_ATIME_CORR);
>+ /* dummy read to update the timer. */
>+ timecounter_read(&fep->tc);
>
> spin_unlock_irqrestore(&fep->tmreg_lock, flags);
>
>@@ -190,12 +218,19 @@ static int fec_ptp_adjtime(struct ptp_clock_info
>*ptp, s64 delta)
> container_of(ptp, struct fec_enet_private, ptp_caps);
> unsigned long flags;
> u64 now;
>+ u32 counter;
>
> spin_lock_irqsave(&fep->tmreg_lock, flags);
>
> now = timecounter_read(&fep->tc);
> now += delta;
>
>+ /* Get the timer value based on adjusted timestamp.
>+ * Update the counter with the masked value.
>+ */
>+ counter = now & fep->cc.mask;
>+ writel(counter, fep->hwp + FEC_ATIME);
>+
> /* reset the timecounter */
> timecounter_init(&fep->tc, &fep->cc, now);
>
>@@ -246,6 +281,7 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
>
> u64 ns;
> unsigned long flags;
>+ u32 counter;
>
> mutex_lock(&fep->ptp_clk_mutex);
> /* Check the ptp clock */
>@@ -256,8 +292,13 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
>
> ns = ts->tv_sec * 1000000000ULL;
> ns += ts->tv_nsec;
>+ /* Get the timer value based on timestamp.
>+ * Update the counter with the masked value.
>+ */
>+ counter = ns & fep->cc.mask;
>
> spin_lock_irqsave(&fep->tmreg_lock, flags);
>+ writel(counter, fep->hwp + FEC_ATIME);
> timecounter_init(&fep->tc, &fep->cc, ns);
> spin_unlock_irqrestore(&fep->tmreg_lock, flags);
> mutex_unlock(&fep->ptp_clk_mutex);
>@@ -396,6 +437,7 @@ void fec_ptp_init(struct platform_device *pdev)
> fep->ptp_caps.enable = fec_ptp_enable;
>
> fep->cycle_speed = clk_get_rate(fep->clk_ptp);
>+ fep->ptp_inc = FEC_T_NSEC_PER_SEC / fep->cycle_speed;
>
> spin_lock_init(&fep->tmreg_lock);
>
>--
>1.9.1
Regards,
Andy
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function.
2014-09-28 4:20 ` [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function Luwei Zhou
@ 2014-09-29 8:56 ` Richard Cochran
2014-09-29 9:38 ` luwei.zhou
0 siblings, 1 reply; 9+ messages in thread
From: Richard Cochran @ 2014-09-29 8:56 UTC (permalink / raw)
To: Luwei Zhou
Cc: davem, netdev, shawn.guo, bhutchings, R49496, b38611, b20596,
stephen
On Sun, Sep 28, 2014 at 12:20:10PM +0800, Luwei Zhou wrote:
> diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
> index 0d8ff3f..2a5ad1c 100644
> --- a/include/linux/ptp_clock_kernel.h
> +++ b/include/linux/ptp_clock_kernel.h
> @@ -49,6 +49,7 @@ struct ptp_clock_request {
> * @n_alarm: The number of programmable alarms.
> * @n_ext_ts: The number of external time stamp channels.
> * @n_per_out: The number of programmable periodic signals.
> + * @n_pps_chan: The number of programmable channles for pps output.
> * @n_pins: The number of programmable pins.
> * @pps: Indicates whether the clock supports a PPS callback.
> * @pin_config: Array of length 'n_pins'. If the number of
> @@ -99,6 +100,7 @@ struct ptp_clock_info {
> int n_alarm;
> int n_ext_ts;
> int n_per_out;
> + int n_pps_chan;
We don't need this. There is only *one* PPS channel.
We only want to select which of 'n_pins' will be the PPS output.
> int n_pins;
> int pps;
> struct ptp_pin_desc *pin_config;
> diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
> index f0b7bfe..57ba7e7 100644
> --- a/include/uapi/linux/ptp_clock.h
> +++ b/include/uapi/linux/ptp_clock.h
> @@ -86,6 +86,7 @@ enum ptp_pin_function {
> PTP_PF_EXTTS,
> PTP_PF_PEROUT,
> PTP_PF_PHYSYNC,
> + PTP_PF_PPS,
> };
>
> struct ptp_pin_desc {
> --
> 1.9.1
>
Thanks,
Richard
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function.
2014-09-29 8:56 ` Richard Cochran
@ 2014-09-29 9:38 ` luwei.zhou
2014-09-29 13:25 ` Richard Cochran
0 siblings, 1 reply; 9+ messages in thread
From: luwei.zhou @ 2014-09-29 9:38 UTC (permalink / raw)
To: Richard Cochran
Cc: davem@davemloft.net, netdev@vger.kernel.org, shawn.guo@linaro.org,
bhutchings@solarflare.com, Fabio.Estevam@freescale.com,
fugang.duan@freescale.com, Frank.Li@freescale.com,
stephen@networkplumber.org
On Mon, Sep 29, 2014,at 4:56:00PM +0800, Richard Cochran wrote:
> -----Original Message-----
> From: Richard Cochran [mailto:richardcochran@gmail.com]
> Sent: Monday, September 29, 2014 4:56 PM
> To: Zhou Luwei-B45643
> Cc: davem@davemloft.net; netdev@vger.kernel.org; shawn.guo@linaro.org;
> bhutchings@solarflare.com; Estevam Fabio-R49496; Duan Fugang-B38611; Li
> Frank-B20596; stephen@networkplumber.org
> Subject: Re: [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to
> ptp_pin_function.
>
> On Sun, Sep 28, 2014 at 12:20:10PM +0800, Luwei Zhou wrote:
>
> > diff --git a/include/linux/ptp_clock_kernel.h
> > b/include/linux/ptp_clock_kernel.h
> > index 0d8ff3f..2a5ad1c 100644
> > --- a/include/linux/ptp_clock_kernel.h
> > +++ b/include/linux/ptp_clock_kernel.h
> > @@ -49,6 +49,7 @@ struct ptp_clock_request {
> > * @n_alarm: The number of programmable alarms.
> > * @n_ext_ts: The number of external time stamp channels.
> > * @n_per_out: The number of programmable periodic signals.
> > + * @n_pps_chan: The number of programmable channles for pps output.
> > * @n_pins: The number of programmable pins.
> > * @pps: Indicates whether the clock supports a PPS callback.
> > * @pin_config: Array of length 'n_pins'. If the number of @@ -99,6
> > +100,7 @@ struct ptp_clock_info {
> > int n_alarm;
> > int n_ext_ts;
> > int n_per_out;
> > + int n_pps_chan;
>
> We don't need this. There is only *one* PPS channel.
>
> We only want to select which of 'n_pins' will be the PPS output.
Yes, there is only one PPS out. But this pin can choose one channel from index (0~3). I am trying to use n_pps_chan to be the max channel index check.
I am a little puzzled. The FEC PTP has 4 channel and we output only one PPS signal. User need to tell driver which channel index is used. In this
Case, n_pins should be 1 or 4?
My understanding is n_pins should be 1 because there is only one PPS output
>
> > int n_pins;
> > int pps;
> > struct ptp_pin_desc *pin_config;
> > diff --git a/include/uapi/linux/ptp_clock.h
> > b/include/uapi/linux/ptp_clock.h index f0b7bfe..57ba7e7 100644
> > --- a/include/uapi/linux/ptp_clock.h
> > +++ b/include/uapi/linux/ptp_clock.h
> > @@ -86,6 +86,7 @@ enum ptp_pin_function {
> > PTP_PF_EXTTS,
> > PTP_PF_PEROUT,
> > PTP_PF_PHYSYNC,
> > + PTP_PF_PPS,
> > };
> >
> > struct ptp_pin_desc {
> > --
> > 1.9.1
> >
>
> Thanks,
> Richard
Thanks,
Luwei
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function.
2014-09-29 9:38 ` luwei.zhou
@ 2014-09-29 13:25 ` Richard Cochran
0 siblings, 0 replies; 9+ messages in thread
From: Richard Cochran @ 2014-09-29 13:25 UTC (permalink / raw)
To: luwei.zhou@freescale.com
Cc: davem@davemloft.net, netdev@vger.kernel.org, shawn.guo@linaro.org,
bhutchings@solarflare.com, Fabio.Estevam@freescale.com,
fugang.duan@freescale.com, Frank.Li@freescale.com,
stephen@networkplumber.org
On Mon, Sep 29, 2014 at 09:38:44AM +0000, luwei.zhou@freescale.com wrote:
> My understanding is n_pins should be 1 because there is only one PPS output
The field 'n_pins' is the number of *physical* *programmable* pins.
If the PPS signal on your SoC can appear on any of 4 different pins,
then n_pins should be 4.
If the PPS signal on your SoC can only appear on one pin,
then n_pins should be 0 (zero).
Please see:
Documentation/ptp/testptp.c
include/linux/ptp_clock_kernel.h
include/uapi/linux/ptp_clock.h
Thanks,
Richard
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2014-09-29 13:25 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-28 4:20 [PATCH v2 0/4] net: fec: ptp: Add FEC PPS ouput support Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 1/4] net: fec: ptp: Use the 31-bit ptp timer Luwei Zhou
2014-09-28 4:20 ` [PATCH v2 2/4] net: fec: ptp: Use hardware algorithm to adjust PTP counter Luwei Zhou
2014-09-29 2:13 ` fugang.duan
2014-09-28 4:20 ` [PATCH v2 3/4] ptp: Add PTP_PF_PPS enumeration to ptp_pin_function Luwei Zhou
2014-09-29 8:56 ` Richard Cochran
2014-09-29 9:38 ` luwei.zhou
2014-09-29 13:25 ` Richard Cochran
2014-09-28 4:20 ` [PATCH v2 4/4] net: fec: ptp: Enalbe PPS ouput based on ptp clock Luwei Zhou
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).