[RFC PATCH v2 1/2] macb: Add 1588 support in Cadence GEM.

Andrei Pistirica andrei.pistirica at microchip.com
Wed Nov 23 05:34:03 PST 2016



On 20.11.2016 20:18, Richard Cochran wrote:
> On Fri, Nov 18, 2016 at 03:21:51PM +0100, Andrei Pistirica wrote:
>> - Frequency adjustment is not directly supported by this IP.
>
> This statement still makes no sense.  Doesn't the following text...

This statement is inherited from original patch, and it refers to the 
fact that it doesn't change directly the frequency, it changes the 
increment value.
I'll remove it to avoid any confusion.

>
>>   addend is the initial value ns increment and similarly addendesub.
>>   The ppb (parts per billion) provided is used as
>>   ns_incr = addend +/- (ppb/rate).
>>   Similarly the remainder of the above is used to populate subns increment.
>
> describe how frequency adjustment is in fact supported?

Ack, I will clarify.

>
>> +config MACB_USE_HWSTAMP
>> +	bool "Use IEEE 1588 hwstamp"
>> +	depends on MACB
>> +	default y
>> +	select PTP_1588_CLOCK
>
> This "select" pattern is going to be replaced with "imply" soon.
>
>    http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1269181.html
>
> You should use the new "imply" key word and reference that series in
> your change log.

Ack.

>
>> diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
>> index 3f385ab..2ee9af8 100644
>> --- a/drivers/net/ethernet/cadence/macb.h
>> +++ b/drivers/net/ethernet/cadence/macb.h
>> @@ -10,6 +10,10 @@
>>  #ifndef _MACB_H
>>  #define _MACB_H
>>
>> +#include <linux/net_tstamp.h>
>> +#include <linux/ptp_clock.h>
>> +#include <linux/ptp_clock_kernel.h>
>
> Don't need net_tstamp.h here.  Move it into the .c file please.

Ack.

>
>> @@ -840,8 +902,26 @@ struct macb {
>>
>>  	unsigned int		rx_frm_len_mask;
>>  	unsigned int		jumbo_max_len;
>> +
>> +#ifdef CONFIG_MACB_USE_HWSTAMP
>> +	unsigned int		hwts_tx_en;
>> +	unsigned int		hwts_rx_en;
>
> These two can be bool'eans.

Ack.

>
>> +	spinlock_t		tsu_clk_lock;
>> +	unsigned int		tsu_rate;
>> +
>> +	struct ptp_clock	*ptp_clock;
>> +	struct ptp_clock_info	ptp_caps;
>> +	unsigned int		ns_incr;
>> +	unsigned int		subns_incr;
>
> These two are 32 bit register values, right?  Then use the u32 type.

Yes. I will make the change.

>
>> +#endif
>>  };
>
>> +static inline void macb_tsu_set_time(struct macb *bp,
>> +				     const struct timespec64 *ts)
>> +{
>> +	u32 ns, sech, secl;
>> +	s64 word_mask = 0xffffffff;
>> +
>> +	sech = (u32)ts->tv_sec;
>> +	secl = (u32)ts->tv_sec;
>> +	ns = ts->tv_nsec;
>> +	if (ts->tv_sec > word_mask)
>> +		sech = (ts->tv_sec >> 32);
>> +
>> +	spin_lock(&bp->tsu_clk_lock);
>> +
>> +	/* TSH doesn't latch the time and no atomicity! */
>> +	gem_writel(bp, TSH, sech);
>> +	gem_writel(bp, TSL, secl);
>
> If TN overflows here then the clock will be off by one whole second!
> Why not clear TN first?

Ack.

>
>> +	gem_writel(bp, TN, ns);
>> +
>> +	spin_unlock(&bp->tsu_clk_lock);
>> +}
>> +
>> +static int macb_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
>> +{
>> +	struct macb *bp = container_of(ptp, struct macb, ptp_caps);
>> +	u32 addend, addend_frac, rem;
>> +	u64 drift_tmr, diff, diff_frac = 0;
>> +	int neg_adj = 0;
>> +
>> +	if (ppb < 0) {
>> +		neg_adj = 1;
>> +		ppb = -ppb;
>> +	}
>> +
>> +	/* drift/period */
>> +	drift_tmr = (bp->ns_incr * ppb) +
>> +		    ((bp->subns_incr * ppb) >> 16);
>
> What?  Why the 16 bit shift?  Last time your said it was 24 bits.

SAMA5D2/3/4 uses GEM-PTP version (16bit), while Harini wrote a driver
for GEM-GXL (24bit). Probably will be a patch on top of this one for 
GXL. This confusion was because I tried to keep the original patch 
unchanged.

>
>> +	/* drift/cycle */
>> +	diff = div_u64_rem(drift_tmr, 1000000000ULL, &rem);
>> +
>> +	/* drift fraction/cycle, if necessary */
>> +	if (rem) {
>> +		u64 fraction = rem;
>> +		fraction = fraction << 16;
>> +
>> +		diff_frac = div_u64(fraction, 1000000000ULL);
>
> If you use a combined tuning word like I explained last time, then you
> will not need a second division.

 From what I understand, your suggestion is:
(ns | frac) * ppb = (total_ns | total_frac)
(total_ns | total_frac) / 10^9 = (adj_ns | adj_frac)
This is correct iff total_ns/10^9 >= 1, but the problem is that there 
are missed fractions due to the following approximation:
frac*ppb =~ 
(ns*ppb+frac*ppb*2^16)*2^16-10^9*2^16*flor(ns*ppb+frac*ppb*2^16, 10^9).

An example which uses values from a real test:
let ppb=4891, ns=12 and frac=3158
- using suggested algorithm, yields: adj_ns = 0 and adj_frac = 0
- using in-place algorithm, yields: adj_ns = 0, adj_frac = 4
You can check the calculus.

Therefore, I would like to keep the in-place algorithm.

>
> Also, please use the new adjfine() PHC method, as adjfreq() is now deprecated.

Yes, I will port the patches on net-next and use adjfine.

>
>> +	}
>> +
>> +	/* adjustmets */
>> +	addend = neg_adj ? (bp->ns_incr - diff) : (bp->ns_incr + diff);
>> +	addend_frac = neg_adj ? (bp->subns_incr - diff_frac) :
>> +				(bp->subns_incr + diff_frac);
>> +
>> +	spin_lock(&bp->tsu_clk_lock);
>> +
>> +	gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, addend_frac));
>> +	gem_writel(bp, TI, GEM_BF(NSINCR, addend));
>> +
>> +	spin_unlock(&bp->tsu_clk_lock);
>> +	return 0;
>> +}
>
>> +void macb_ptp_init(struct net_device *ndev)
>> +{
>> +	struct macb *bp = netdev_priv(ndev);
>> +	struct timespec64 now;
>> +	u32 rem = 0;
>> +
>> +	if (!(bp->caps | MACB_CAPS_GEM_HAS_PTP)){
>> +		netdev_vdbg(bp->dev, "Platform does not support PTP!\n");
>> +		return;
>> +	}
>> +
>> +	spin_lock_init(&bp->tsu_clk_lock);
>> +
>> +	bp->ptp_caps = macb_ptp_caps;
>> +	bp->tsu_rate = clk_get_rate(bp->pclk);
>> +
>> +	getnstimeofday64(&now);
>> +	macb_tsu_set_time(bp, (const struct timespec64 *)&now);
>> +
>> +	bp->ns_incr = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem);
>> +	if (rem) {
>> +		u64 adj = rem;
>> +		/* Multiply by 2^16 as subns register is 16 bits */
>
> Last time you said:
>> +		/* Multiple by 2^24 as subns field is 24 bits */
>
>> +		adj <<= 16;
>> +		bp->subns_incr = div_u64(adj, bp->tsu_rate);
>> +	} else {
>> +		bp->subns_incr = 0;
>> +	}
>> +
>> +	gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, bp->subns_incr));
>> +	gem_writel(bp, TI, GEM_BF(NSINCR, bp->ns_incr));
>> +	gem_writel(bp, TA, 0);
>> +
>> +	bp->ptp_clock = ptp_clock_register(&bp->ptp_caps, NULL);
>
> You call regsiter, but you never call unregister!

Ack. I did a stupid mistake... sorry.

>
>> +	if (IS_ERR(&bp->ptp_clock)) {
>> +		bp->ptp_clock = NULL;
>> +		pr_err("ptp clock register failed\n");
>> +		return;
>> +	}
>> +
>> +	dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", GMAC_TIMER_NAME);
>> +}
>> +
>> --
>> 1.9.1
>>
>
> Thanks,
> Richard
>

Regards,
Andrei



More information about the linux-arm-kernel mailing list