Message ID | 1481134912-2243-1-git-send-email-andrei.pistirica@microchip.com |
---|---|
State | RFC, archived |
Delegated to: | David Miller |
Headers | show |
On Wed, Dec 07, 2016 at 08:21:51PM +0200, Andrei Pistirica wrote: > +#ifdef CONFIG_MACB_USE_HWSTAMP > +void gem_ptp_init(struct net_device *ndev); > +void gem_ptp_remove(struct net_device *ndev); > + > +void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb); > +void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb); These are in the hot path, and so you should do the test before calling the global function, something like this: void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb); static void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) { if (!bp->hwts_tx_en) return; gem_ptp_txstamp(bp, skb); } Ditto for Rx. > +#else > +static inline void gem_ptp_init(struct net_device *ndev) { } > +static inline void gem_ptp_remove(struct net_device *ndev) { } > + > +static inline void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) { } > +static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb) { } > +#endif > + > +static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) > +{ > + struct macb *bp = container_of(ptp, struct macb, ptp_caps); > + u32 word, diff; > + u64 adj, rate; > + int neg_adj = 0; > + > + if (scaled_ppm < 0) { > + neg_adj = 1; > + scaled_ppm = -scaled_ppm; > + } > + rate = scaled_ppm; > + > + /* word: unused(8bit) | ns(8bit) | fractions(16bit) */ > + word = (bp->ns_incr << 16) + bp->subns_incr; > + > + adj = word; > + adj *= rate; > + adj >>= 16; /* remove fractions */ > + adj += 500000UL; > + diff = div_u64(adj, 1000000UL); In order to round correctly, shouldn't this be? adj *= rate; adj += 500000UL << 16; adj >>= 16; diff = div_u64(adj, 1000000UL); > + word = neg_adj ? word - diff : word + diff; > + > + spin_lock(&bp->tsu_clk_lock); > + > + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, (word & 0xffff))); > + gem_writel(bp, TI, GEM_BF(NSINCR, (word >> 16))); > + > + spin_unlock(&bp->tsu_clk_lock); > + return 0; > +} > +static s32 gem_ptp_max_adj(unsigned int f_nom) > +{ > + u64 adj; > + > + /* The 48 bits of seconds for the GEM overflows every: > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), > + * thus the maximum adjust frequency must not overflow CNS register: > + * > + * addend = 10^9/nominal_freq > + * adj_max = +/- addend*ppb_max/10^9 > + * max_ppb = (2^8-1)*nominal_freq-10^9 > + */ > + adj = f_nom; > + adj *= 0xffff; > + adj -= 1000000000ULL; What is this computation, and how does it relate to the comment? > + return adj; > +} > +/* While GEM can timestamp PTP packets, it does not mark the RX descriptor Does it timestamp PTP event packets only, or all packets? (See my comment in patch 2/2) > + * to identify them. UDP packets must be parsed to identify PTP packets. > + * > + * Note: Inspired from drivers/net/ethernet/ti/cpts.c > + */ Thanks, Richard
On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: > > +static s32 gem_ptp_max_adj(unsigned int f_nom) > > +{ > > + u64 adj; > > + > > + /* The 48 bits of seconds for the GEM overflows every: > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), > > + * thus the maximum adjust frequency must not overflow CNS register: > > + * > > + * addend = 10^9/nominal_freq > > + * adj_max = +/- addend*ppb_max/10^9 > > + * max_ppb = (2^8-1)*nominal_freq-10^9 > > + */ > > + adj = f_nom; > > + adj *= 0xffff; > > + adj -= 1000000000ULL; > > What is this computation, and how does it relate to the comment? I am not sure what you meant, but it sounds like you are on the wrong track. Let me explain... The max_adj has nothing at all to do with the width of the time register. Rather, it should reflect the maximum possible change in the tuning word. For example, with a nominal 8 ns period, the tuning word is 0x80000. Looking at running the clock more slowly, the slowest possible word is 0x00001, meaning a difference of 0x7FFFF. This implies an adjustment of 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can already have 0x100000, twice as fast, or just under 2 billion ppb. You should consider the extreme cases to determine the most limited (smallest) max_adj value: Case 1 - high frequency ~~~~~~~~~~~~~~~~~~~~~~~ With a nominal 1 ns period, we have the nominal tuning word 0x10000. The smallest is 0x1 for a difference of 0xFFFF. This corresponds to an adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. Case 2 - low frequency ~~~~~~~~~~~~~~~~~~~~~~ With a nominal 255 ns period, the nominal word is 0xFF0000, the largest 0xFFFFFF, and the difference is 0xFFFF. This corresponds to and adjustment of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. Since 3921508 ppb is a huge adjustment, you can simply use that as a safe maximum, ignoring the actual input clock. Thanks, Richard
Le 07/12/2016 à 20:39, Richard Cochran a écrit : > On Wed, Dec 07, 2016 at 08:21:51PM +0200, Andrei Pistirica wrote: >> +#ifdef CONFIG_MACB_USE_HWSTAMP >> +void gem_ptp_init(struct net_device *ndev); >> +void gem_ptp_remove(struct net_device *ndev); >> + >> +void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb); >> +void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb); > > These are in the hot path, and so you should do the test before > calling the global function, something like this: > > void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb); > > static void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) > { > if (!bp->hwts_tx_en) > return; > gem_ptp_txstamp(bp, skb); > } > > Ditto for Rx. Hi Richard, So you mean that as the "global" function won't be "inlined" by the compiler as the function is not "static" neither in the same file and that the jump will be implemented anyway. And this even if the function is only called at a single location... This way, if we add a kind or accessors function like the one that you propose, with the test in it, the branch prediction can play his role without breaking the processor pipeline as the accessors function will be inlined by the compiler: Am I right? So, yes, makes sense. Thanks for the hint. Regards,
> -----Original Message----- > From: Richard Cochran [mailto:richardcochran@gmail.com] > Sent: Wednesday, December 07, 2016 11:04 PM > To: Andrei Pistirica - M16132 > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- > kernel@lists.infradead.org; davem@davemloft.net; > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; > anirudh@xilinx.com; boris.brezillon@free-electrons.com; > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; > rafalo@cadence.com > Subject: Re: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in > Cadence GEM. > > On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: > > > +static s32 gem_ptp_max_adj(unsigned int f_nom) { > > > + u64 adj; > > > + > > > + /* The 48 bits of seconds for the GEM overflows every: > > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), > > > + * thus the maximum adjust frequency must not overflow CNS > register: > > > + * > > > + * addend = 10^9/nominal_freq > > > + * adj_max = +/- addend*ppb_max/10^9 > > > + * max_ppb = (2^8-1)*nominal_freq-10^9 > > > + */ > > > + adj = f_nom; > > > + adj *= 0xffff; > > > + adj -= 1000000000ULL; > > > > What is this computation, and how does it relate to the comment? I considered the following simple equation: increment value at nominal frequency (which is 10^9/nominal frequency nsecs) + the maximum drift value (nsecs) <= maximum increment value at nominal frequency (which is 8bit:0xffff). If maximum drift is written as function of nominal frequency and maximum ppb, then the equation above yields that the maximum ppb is: (2^8 - 1) *nominal_frequency - 10^9. The equation is also simplified by the fact that the drift is written as ppm + 16bit_fractions and the increment value is written as nsec + 16bit_fractions. Rafal said that this value is hardcoded: 0x64E6, while Harini said: 250000000. I need to dig into this... > > I am not sure what you meant, but it sounds like you are on the wrong track. > Let me explain... Thanks. > > The max_adj has nothing at all to do with the width of the time register. > Rather, it should reflect the maximum possible change in the tuning word. > > For example, with a nominal 8 ns period, the tuning word is 0x80000. > Looking at running the clock more slowly, the slowest possible word is > 0x00001, meaning a difference of 0x7FFFF. This implies an adjustment of > 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can already > have 0x100000, twice as fast, or just under 2 billion ppb. > > You should consider the extreme cases to determine the most limited > (smallest) max_adj value: > > Case 1 - high frequency > ~~~~~~~~~~~~~~~~~~~~~~~ > > With a nominal 1 ns period, we have the nominal tuning word 0x10000. > The smallest is 0x1 for a difference of 0xFFFF. This corresponds to an > adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. > > Case 2 - low frequency > ~~~~~~~~~~~~~~~~~~~~~~ > > With a nominal 255 ns period, the nominal word is 0xFF0000, the largest > 0xFFFFFF, and the difference is 0xFFFF. This corresponds to and adjustment > of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. > > Since 3921508 ppb is a huge adjustment, you can simply use that as a safe > maximum, ignoring the actual input clock. > > Thanks, > Richard > > Regards, Andrei
Hi, On Thu, Dec 8, 2016 at 8:11 PM, <Andrei.Pistirica@microchip.com> wrote: > > >> -----Original Message----- >> From: Richard Cochran [mailto:richardcochran@gmail.com] >> Sent: Wednesday, December 07, 2016 11:04 PM >> To: Andrei Pistirica - M16132 >> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- >> kernel@lists.infradead.org; davem@davemloft.net; >> nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; >> harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; >> anirudh@xilinx.com; boris.brezillon@free-electrons.com; >> alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; >> rafalo@cadence.com >> Subject: Re: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in >> Cadence GEM. >> >> On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: >> > > +static s32 gem_ptp_max_adj(unsigned int f_nom) { >> > > + u64 adj; >> > > + >> > > + /* The 48 bits of seconds for the GEM overflows every: >> > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), >> > > + * thus the maximum adjust frequency must not overflow CNS >> register: >> > > + * >> > > + * addend = 10^9/nominal_freq >> > > + * adj_max = +/- addend*ppb_max/10^9 >> > > + * max_ppb = (2^8-1)*nominal_freq-10^9 >> > > + */ >> > > + adj = f_nom; >> > > + adj *= 0xffff; >> > > + adj -= 1000000000ULL; >> > >> > What is this computation, and how does it relate to the comment? > > I considered the following simple equation: increment value at nominal frequency (which is 10^9/nominal frequency nsecs) + the maximum drift value (nsecs) <= maximum increment value at nominal frequency (which is 8bit:0xffff). > If maximum drift is written as function of nominal frequency and maximum ppb, then the equation above yields that the maximum ppb is: (2^8 - 1) *nominal_frequency - 10^9. The equation is also simplified by the fact that the drift is written as ppm + 16bit_fractions and the increment value is written as nsec + 16bit_fractions. > > Rafal said that this value is hardcoded: 0x64E6, while Harini said: 250000000. @ Andrei, I may have equated max ppb to max tsu frequency allowed on the system and set that. That will be wrong. > > I need to dig into this... > >> >> I am not sure what you meant, but it sounds like you are on the wrong track. >> Let me explain... > > Thanks. > >> >> The max_adj has nothing at all to do with the width of the time register. >> Rather, it should reflect the maximum possible change in the tuning word. >> >> For example, with a nominal 8 ns period, the tuning word is 0x80000. >> Looking at running the clock more slowly, the slowest possible word is >> 0x00001, meaning a difference of 0x7FFFF. This implies an adjustment of >> 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can already >> have 0x100000, twice as fast, or just under 2 billion ppb. >> >> You should consider the extreme cases to determine the most limited >> (smallest) max_adj value: >> >> Case 1 - high frequency >> ~~~~~~~~~~~~~~~~~~~~~~~ >> >> With a nominal 1 ns period, we have the nominal tuning word 0x10000. >> The smallest is 0x1 for a difference of 0xFFFF. This corresponds to an >> adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. >> >> Case 2 - low frequency >> ~~~~~~~~~~~~~~~~~~~~~~ >> >> With a nominal 255 ns period, the nominal word is 0xFF0000, the largest >> 0xFFFFFF, and the difference is 0xFFFF. This corresponds to and adjustment >> of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. >> >> Since 3921508 ppb is a huge adjustment, you can simply use that as a safe >> maximum, ignoring the actual input clock. >> Thanks Richard. So, if I understand right, this is theoretically limited by the maximum input clock: So if the highest frequency allowed (also commonly sourced in my case) is 200MHz, then with a 5ns time period, considering the adjustment to slowest possible word, 0x4FFFF/0x50000 will be 999996948 ppb. Shouldn't this be the max_adj? I'm afraid I don't get why we are choosing the most limited max adj.. Sorry if I'm missing something - could you please help me understand? Regards, Harini
On Fri, Dec 09, 2016 at 11:07:23AM +0530, Harini Katakam wrote: > I'm afraid I don't get why we are choosing the most limited max adj.. > Sorry if I'm missing something - could you please help me understand? This max_adj is only important when the local clock offset is large and user space chooses not to step the time value. In that case, user space will want to run the clock as fast (or as slow) as possible in order to catch up with the remote clock. The driver must provide a max_adj that is always safe for user space to apply via the clock_adjtime() system call. HTH, Richard
-----Original Message----- > From: Andrei.Pistirica@microchip.com [mailto:Andrei.Pistirica@microchip.com] > Sent: 8 grudnia 2016 15:42 > To: richardcochran@gmail.com > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; davem@davemloft.net; nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; anirudh@xilinx.com; boris.brezillon@free-electrons.com; alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; Rafal Ozieblo > Subject: RE: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in Cadence GEM. > > > > > -----Original Message----- > > From: Richard Cochran [mailto:richardcochran@gmail.com] > > Sent: Wednesday, December 07, 2016 11:04 PM > > To: Andrei Pistirica - M16132 > > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- > > kernel@lists.infradead.org; davem@davemloft.net; > > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; > > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; > > anirudh@xilinx.com; boris.brezillon@free-electrons.com; > > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; > > rafalo@cadence.com > > Subject: Re: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in > > Cadence GEM. > > > > On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: > > > > +static s32 gem_ptp_max_adj(unsigned int f_nom) { > > > > + u64 adj; > > > > + > > > > + /* The 48 bits of seconds for the GEM overflows every: > > > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), > > > > + * thus the maximum adjust frequency must not overflow CNS > > register: > > > > + * > > > > + * addend = 10^9/nominal_freq > > > > + * adj_max = +/- addend*ppb_max/10^9 > > > > + * max_ppb = (2^8-1)*nominal_freq-10^9 > > > > + */ > > > > + adj = f_nom; > > > > + adj *= 0xffff; > > > > + adj -= 1000000000ULL; > > > > > > What is this computation, and how does it relate to the comment? > > I considered the following simple equation: increment value at nominal frequency (which is 10^9/nominal frequency nsecs) + the maximum drift value (nsecs) <= maximum increment value at nominal frequency (which is 8bit:0xffff). > If maximum drift is written as function of nominal frequency and maximum ppb, then the equation above yields that the maximum ppb is: (2^8 - 1) *nominal_frequency - 10^9. The equation is also simplified by the fact that the drift is written as ppm + 16bit_fractions and the increment value is written as nsec + 16bit_fractions. > > Rafal said that this value is hardcoded: 0x64E6, while Harini said: 250000000. To clarify a little bit. In my reference code this value (0x64E6) was taken from our legacy code. It was used for testing only. I know it should be change to something more accurate. This is the reason why I asked how did you count it (250000000). According to our calculations this value depends on actual set period (incr_ns and incr_sub_ns) and min and max value we can set. The calculation were a little bit intricate, so we decided to leave it as it was. > > I need to dig into this... > > > > > I am not sure what you meant, but it sounds like you are on the wrong track. > > Let me explain... > > Thanks. > > > > > The max_adj has nothing at all to do with the width of the time register. > > Rather, it should reflect the maximum possible change in the tuning word. > > > > For example, with a nominal 8 ns period, the tuning word is 0x80000. > > Looking at running the clock more slowly, the slowest possible word is > > 0x00001, meaning a difference of 0x7FFFF. This implies an adjustment > > of > > 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can > > already have 0x100000, twice as fast, or just under 2 billion ppb. > > > > You should consider the extreme cases to determine the most limited > > (smallest) max_adj value: > > > > Case 1 - high frequency > > ~~~~~~~~~~~~~~~~~~~~~~~ > > > > With a nominal 1 ns period, we have the nominal tuning word 0x10000. > > The smallest is 0x1 for a difference of 0xFFFF. This corresponds to > > an adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. > > > > Case 2 - low frequency > > ~~~~~~~~~~~~~~~~~~~~~~ > > > > With a nominal 255 ns period, the nominal word is 0xFF0000, the > > largest 0xFFFFFF, and the difference is 0xFFFF. This corresponds to > > and adjustment of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. > > > > Since 3921508 ppb is a huge adjustment, you can simply use that as a > > safe maximum, ignoring the actual input clock. > > > > Thanks, > > Richard > > > > > > Regards, > Andrei > Best regards, Rafal Ozieblo | Firmware System Engineer, phone nbr.: +48 32 5085469 www.cadence.com
> -----Original Message----- > From: Rafal Ozieblo [mailto:rafalo@cadence.com] > Sent: Friday, December 09, 2016 11:20 AM > To: Andrei Pistirica - M16132; richardcochran@gmail.com > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- > kernel@lists.infradead.org; davem@davemloft.net; > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; > anirudh@xilinx.com; boris.brezillon@free-electrons.com; > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com > Subject: RE: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in Cadence > GEM. > > -----Original Message----- > > From: Andrei.Pistirica@microchip.com > > [mailto:Andrei.Pistirica@microchip.com] > > Sent: 8 grudnia 2016 15:42 > > To: richardcochran@gmail.com > > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; > > linux-arm-kernel@lists.infradead.org; davem@davemloft.net; > > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; > > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; > > anirudh@xilinx.com; boris.brezillon@free-electrons.com; > > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; Rafal > > Ozieblo > > Subject: RE: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in > Cadence GEM. > > > > > > > > > -----Original Message----- > > > From: Richard Cochran [mailto:richardcochran@gmail.com] > > > Sent: Wednesday, December 07, 2016 11:04 PM > > > To: Andrei Pistirica - M16132 > > > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- > > > kernel@lists.infradead.org; davem@davemloft.net; > > > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; > > > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; > > > anirudh@xilinx.com; boris.brezillon@free-electrons.com; > > > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; > > > rafalo@cadence.com > > > Subject: Re: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in > > > Cadence GEM. > > > > > > On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: > > > > > +static s32 gem_ptp_max_adj(unsigned int f_nom) { > > > > > + u64 adj; > > > > > + > > > > > + /* The 48 bits of seconds for the GEM overflows every: > > > > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), > > > > > + * thus the maximum adjust frequency must not overflow > > > > > + CNS > > > register: > > > > > + * > > > > > + * addend = 10^9/nominal_freq > > > > > + * adj_max = +/- addend*ppb_max/10^9 > > > > > + * max_ppb = (2^8-1)*nominal_freq-10^9 > > > > > + */ > > > > > + adj = f_nom; > > > > > + adj *= 0xffff; > > > > > + adj -= 1000000000ULL; > > > > > > > > What is this computation, and how does it relate to the comment? > > > > I considered the following simple equation: increment value at nominal > frequency (which is 10^9/nominal frequency nsecs) + the maximum drift > value (nsecs) <= maximum increment value at nominal frequency (which is > 8bit:0xffff). > > If maximum drift is written as function of nominal frequency and > maximum ppb, then the equation above yields that the maximum ppb is: > (2^8 - 1) *nominal_frequency - 10^9. The equation is also simplified by the > fact that the drift is written as ppm + 16bit_fractions and the increment > value is written as nsec + 16bit_fractions. > > > > Rafal said that this value is hardcoded: 0x64E6, while Harini said: > 250000000. > > To clarify a little bit. In my reference code this value (0x64E6) was taken > from our legacy code. It was used for testing only. I know it should be > change to something more accurate. This is the reason why I asked how did > you count it (250000000). According to our calculations this value depends > on actual set period (incr_ns and incr_sub_ns) and min and max value we > can set. The calculation were a little bit intricate, so we decided to leave it > as it was. > > > > > I need to dig into this... > > > > > > > > I am not sure what you meant, but it sounds like you are on the wrong > track. > > > Let me explain... > > > > Thanks. > > > > > > > > The max_adj has nothing at all to do with the width of the time register. > > > Rather, it should reflect the maximum possible change in the tuning > word. > > > > > > For example, with a nominal 8 ns period, the tuning word is 0x80000. > > > Looking at running the clock more slowly, the slowest possible word > > > is 0x00001, meaning a difference of 0x7FFFF. This implies an > > > adjustment of > > > 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can > > > already have 0x100000, twice as fast, or just under 2 billion ppb. > > > > > > You should consider the extreme cases to determine the most limited > > > (smallest) max_adj value: > > > > > > Case 1 - high frequency > > > ~~~~~~~~~~~~~~~~~~~~~~~ > > > > > > With a nominal 1 ns period, we have the nominal tuning word 0x10000. > > > The smallest is 0x1 for a difference of 0xFFFF. This corresponds to > > > an adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. > > > > > > Case 2 - low frequency > > > ~~~~~~~~~~~~~~~~~~~~~~ > > > > > > With a nominal 255 ns period, the nominal word is 0xFF0000, the > > > largest 0xFFFFFF, and the difference is 0xFFFF. This corresponds to > > > and adjustment of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. > > > > > > Since 3921508 ppb is a huge adjustment, you can simply use that as a > > > safe maximum, ignoring the actual input clock. > > > > > > Thanks, > > > Richard > > > > > > > > > > Regards, > > Andrei > > > > Best regards, > Rafal Ozieblo | Firmware System Engineer, > phone nbr.: +48 32 5085469 > www.cadence.com Hi Guys, Based on Richard's input, this is what I want to do for our platforms: struct macb_ptp_info { void (*ptp_init)(struct net_device *ndev); void (*ptp_remove)(struct net_device *ndev); + s32 (*get_ptp_max_adj)(void); unsigned int (*get_tsu_rate)(struct macb *bp); int (*get_ts_info)(struct net_device *dev, struct ethtool_ts_info *info); int (*get_hwtst)(struct net_device *netdev, struct ifreq *ifr); int (*set_hwtst)(struct net_device *netdev, struct ifreq *ifr, int cmd); }; +static s32 gem_get_ptp_max_adj(void) +{ + return 3921508; +} static struct macb_ptp_info gem_ptp_info = { .ptp_init = gem_ptp_init, .ptp_remove = gem_ptp_remove, + .get_ptp_max_adj = gem_get_ptp_max_adj, .get_tsu_rate = gem_get_tsu_rate, .get_ts_info = gem_get_ts_info, .get_hwtst = gem_get_hwtst, .set_hwtst = gem_set_hwtst, }; [...] void gem_ptp_init(struct net_device *ndev) { [...] /* nominal frequency and maximum adjustment in ppb */ bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); + bp->ptp_caps.max_adj = bp->ptp_info->get_ptp_max_adj(); [...] } Richard, are you agree with this? Harini, you can fill the callback with the value for your platform. Tell me if you are ok with function's signature. Regards, Andrei
Hi Andrei, On Mon, Dec 12, 2016 at 3:52 PM, <Andrei.Pistirica@microchip.com> wrote: > > >> -----Original Message----- >> From: Rafal Ozieblo [mailto:rafalo@cadence.com] >> Sent: Friday, December 09, 2016 11:20 AM >> To: Andrei Pistirica - M16132; richardcochran@gmail.com >> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- >> kernel@lists.infradead.org; davem@davemloft.net; >> nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; >> harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; >> anirudh@xilinx.com; boris.brezillon@free-electrons.com; >> alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com >> Subject: RE: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in Cadence >> GEM. >> >> -----Original Message----- >> > From: Andrei.Pistirica@microchip.com >> > [mailto:Andrei.Pistirica@microchip.com] >> > Sent: 8 grudnia 2016 15:42 >> > To: richardcochran@gmail.com >> > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; >> > linux-arm-kernel@lists.infradead.org; davem@davemloft.net; >> > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; >> > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; >> > anirudh@xilinx.com; boris.brezillon@free-electrons.com; >> > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; Rafal >> > Ozieblo >> > Subject: RE: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in >> Cadence GEM. >> > >> > >> > >> > > -----Original Message----- >> > > From: Richard Cochran [mailto:richardcochran@gmail.com] >> > > Sent: Wednesday, December 07, 2016 11:04 PM >> > > To: Andrei Pistirica - M16132 >> > > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm- >> > > kernel@lists.infradead.org; davem@davemloft.net; >> > > nicolas.ferre@atmel.com; harinikatakamlinux@gmail.com; >> > > harini.katakam@xilinx.com; punnaia@xilinx.com; michals@xilinx.com; >> > > anirudh@xilinx.com; boris.brezillon@free-electrons.com; >> > > alexandre.belloni@free-electrons.com; tbultel@pixelsurmer.com; >> > > rafalo@cadence.com >> > > Subject: Re: [RFC PATCH net-next v3 1/2] macb: Add 1588 support in >> > > Cadence GEM. >> > > >> > > On Wed, Dec 07, 2016 at 08:39:09PM +0100, Richard Cochran wrote: >> > > > > +static s32 gem_ptp_max_adj(unsigned int f_nom) { >> > > > > + u64 adj; >> > > > > + >> > > > > + /* The 48 bits of seconds for the GEM overflows every: >> > > > > + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), >> > > > > + * thus the maximum adjust frequency must not overflow >> > > > > + CNS >> > > register: >> > > > > + * >> > > > > + * addend = 10^9/nominal_freq >> > > > > + * adj_max = +/- addend*ppb_max/10^9 >> > > > > + * max_ppb = (2^8-1)*nominal_freq-10^9 >> > > > > + */ >> > > > > + adj = f_nom; >> > > > > + adj *= 0xffff; >> > > > > + adj -= 1000000000ULL; >> > > > >> > > > What is this computation, and how does it relate to the comment? >> > >> > I considered the following simple equation: increment value at nominal >> frequency (which is 10^9/nominal frequency nsecs) + the maximum drift >> value (nsecs) <= maximum increment value at nominal frequency (which is >> 8bit:0xffff). >> > If maximum drift is written as function of nominal frequency and >> maximum ppb, then the equation above yields that the maximum ppb is: >> (2^8 - 1) *nominal_frequency - 10^9. The equation is also simplified by the >> fact that the drift is written as ppm + 16bit_fractions and the increment >> value is written as nsec + 16bit_fractions. >> > >> > Rafal said that this value is hardcoded: 0x64E6, while Harini said: >> 250000000. >> >> To clarify a little bit. In my reference code this value (0x64E6) was taken >> from our legacy code. It was used for testing only. I know it should be >> change to something more accurate. This is the reason why I asked how did >> you count it (250000000). According to our calculations this value depends >> on actual set period (incr_ns and incr_sub_ns) and min and max value we >> can set. The calculation were a little bit intricate, so we decided to leave it >> as it was. >> >> > >> > I need to dig into this... >> > >> > > >> > > I am not sure what you meant, but it sounds like you are on the wrong >> track. >> > > Let me explain... >> > >> > Thanks. >> > >> > > >> > > The max_adj has nothing at all to do with the width of the time register. >> > > Rather, it should reflect the maximum possible change in the tuning >> word. >> > > >> > > For example, with a nominal 8 ns period, the tuning word is 0x80000. >> > > Looking at running the clock more slowly, the slowest possible word >> > > is 0x00001, meaning a difference of 0x7FFFF. This implies an >> > > adjustment of >> > > 0x7FFFF/0x80000 or 999998092 ppb. Running more quickly, we can >> > > already have 0x100000, twice as fast, or just under 2 billion ppb. >> > > >> > > You should consider the extreme cases to determine the most limited >> > > (smallest) max_adj value: >> > > >> > > Case 1 - high frequency >> > > ~~~~~~~~~~~~~~~~~~~~~~~ >> > > >> > > With a nominal 1 ns period, we have the nominal tuning word 0x10000. >> > > The smallest is 0x1 for a difference of 0xFFFF. This corresponds to >> > > an adjustment of 0xFFFF/0x10000 = .9999847412109375 or 999984741 ppb. >> > > >> > > Case 2 - low frequency >> > > ~~~~~~~~~~~~~~~~~~~~~~ >> > > >> > > With a nominal 255 ns period, the nominal word is 0xFF0000, the >> > > largest 0xFFFFFF, and the difference is 0xFFFF. This corresponds to >> > > and adjustment of 0xFFFF/0xFF0000 = .0039215087890625 or 3921508 ppb. >> > > >> > > Since 3921508 ppb is a huge adjustment, you can simply use that as a >> > > safe maximum, ignoring the actual input clock. >> > > >> > > Thanks, >> > > Richard >> > > >> > > >> > >> > Regards, >> > Andrei >> > >> >> Best regards, >> Rafal Ozieblo | Firmware System Engineer, >> phone nbr.: +48 32 5085469 >> www.cadence.com > > Hi Guys, > > Based on Richard's input, this is what I want to do for our platforms: > > struct macb_ptp_info { > void (*ptp_init)(struct net_device *ndev); > void (*ptp_remove)(struct net_device *ndev); > + s32 (*get_ptp_max_adj)(void); > unsigned int (*get_tsu_rate)(struct macb *bp); > int (*get_ts_info)(struct net_device *dev, > struct ethtool_ts_info *info); > int (*get_hwtst)(struct net_device *netdev, > struct ifreq *ifr); > int (*set_hwtst)(struct net_device *netdev, > struct ifreq *ifr, int cmd); > }; > > +static s32 gem_get_ptp_max_adj(void) > +{ > + return 3921508; > +} > > static struct macb_ptp_info gem_ptp_info = { > .ptp_init = gem_ptp_init, > .ptp_remove = gem_ptp_remove, > + .get_ptp_max_adj = gem_get_ptp_max_adj, > .get_tsu_rate = gem_get_tsu_rate, > .get_ts_info = gem_get_ts_info, > .get_hwtst = gem_get_hwtst, > .set_hwtst = gem_set_hwtst, > }; > > [...] > void gem_ptp_init(struct net_device *ndev) > { > [...] > /* nominal frequency and maximum adjustment in ppb */ > bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); > + bp->ptp_caps.max_adj = bp->ptp_info->get_ptp_max_adj(); > [...] > } > > Richard, are you agree with this? > > Harini, you can fill the callback with the value for your platform. Tell me if you are ok with function's signature. > Thanks, this works for me. Regards, Harini
On Mon, Dec 12, 2016 at 10:22:43AM +0000, Andrei.Pistirica@microchip.com wrote:
> Richard, are you agree with this?
Yes, but please trim your replies next time. Scrolling through pages
of quoted headers and stale content in order to read one line is very
annoying.
Thanks,
Richard
diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index f0bcb15..ebbc65f 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -29,6 +29,14 @@ config MACB support for the MACB/GEM chip. To compile this driver as a module, choose M here: the module - will be called macb. + will be called cadence-macb. + +config MACB_USE_HWSTAMP + bool "Use IEEE 1588 hwstamp" + depends on MACB + default y + select PTP_1588_CLOCK + ---help--- + Enable IEEE 1588 Precision Time Protocol (PTP) support for MACB. endif # NET_CADENCE diff --git a/drivers/net/ethernet/cadence/Makefile b/drivers/net/ethernet/cadence/Makefile index 91f79b1..4402d42 100644 --- a/drivers/net/ethernet/cadence/Makefile +++ b/drivers/net/ethernet/cadence/Makefile @@ -2,4 +2,10 @@ # Makefile for the Atmel network device drivers. # -obj-$(CONFIG_MACB) += macb.o +cadence-macb-y := macb.o + +ifeq ($(CONFIG_MACB_USE_HWSTAMP),y) +cadence-macb-y += macb_ptp.o +endif + +obj-$(CONFIG_MACB) += cadence-macb.o diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index d67adad..b3688f1 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -10,6 +10,9 @@ #ifndef _MACB_H #define _MACB_H +#include <linux/ptp_clock.h> +#include <linux/ptp_clock_kernel.h> + #define MACB_GREGS_NBR 16 #define MACB_GREGS_VERSION 2 #define MACB_MAX_QUEUES 8 @@ -131,6 +134,20 @@ #define GEM_RXIPCCNT 0x01a8 /* IP header Checksum Error Counter */ #define GEM_RXTCPCCNT 0x01ac /* TCP Checksum Error Counter */ #define GEM_RXUDPCCNT 0x01b0 /* UDP Checksum Error Counter */ +#define GEM_TISUBN 0x01bc /* 1588 Timer Increment Sub-ns */ +#define GEM_TSH 0x01c0 /* 1588 Timer Seconds High */ +#define GEM_TSL 0x01d0 /* 1588 Timer Seconds Low */ +#define GEM_TN 0x01d4 /* 1588 Timer Nanoseconds */ +#define GEM_TA 0x01d8 /* 1588 Timer Adjust */ +#define GEM_TI 0x01dc /* 1588 Timer Increment */ +#define GEM_EFTSL 0x01e0 /* PTP Event Frame Tx Seconds Low */ +#define GEM_EFTN 0x01e4 /* PTP Event Frame Tx Nanoseconds */ +#define GEM_EFRSL 0x01e8 /* PTP Event Frame Rx Seconds Low */ +#define GEM_EFRN 0x01ec /* PTP Event Frame Rx Nanoseconds */ +#define GEM_PEFTSL 0x01f0 /* PTP Peer Event Frame Tx Secs Low */ +#define GEM_PEFTN 0x01f4 /* PTP Peer Event Frame Tx Ns */ +#define GEM_PEFRSL 0x01f8 /* PTP Peer Event Frame Rx Sec Low */ +#define GEM_PEFRN 0x01fc /* PTP Peer Event Frame Rx Ns */ #define GEM_DCFG1 0x0280 /* Design Config 1 */ #define GEM_DCFG2 0x0284 /* Design Config 2 */ #define GEM_DCFG3 0x0288 /* Design Config 3 */ @@ -174,6 +191,7 @@ #define MACB_NCR_TPF_SIZE 1 #define MACB_TZQ_OFFSET 12 /* Transmit zero quantum pause frame */ #define MACB_TZQ_SIZE 1 +#define MACB_SRTSM_OFFSET 15 /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ @@ -319,6 +337,32 @@ #define MACB_PTZ_SIZE 1 #define MACB_WOL_OFFSET 14 /* Enable wake-on-lan interrupt */ #define MACB_WOL_SIZE 1 +#define MACB_DRQFR_OFFSET 18 /* PTP Delay Request Frame Received */ +#define MACB_DRQFR_SIZE 1 +#define MACB_SFR_OFFSET 19 /* PTP Sync Frame Received */ +#define MACB_SFR_SIZE 1 +#define MACB_DRQFT_OFFSET 20 /* PTP Delay Request Frame Transmitted */ +#define MACB_DRQFT_SIZE 1 +#define MACB_SFT_OFFSET 21 /* PTP Sync Frame Transmitted */ +#define MACB_SFT_SIZE 1 +#define MACB_PDRQFR_OFFSET 22 /* PDelay Request Frame Received */ +#define MACB_PDRQFR_SIZE 1 +#define MACB_PDRSFR_OFFSET 23 /* PDelay Response Frame Received */ +#define MACB_PDRSFR_SIZE 1 +#define MACB_PDRQFT_OFFSET 24 /* PDelay Request Frame Transmitted */ +#define MACB_PDRQFT_SIZE 1 +#define MACB_PDRSFT_OFFSET 25 /* PDelay Response Frame Transmitted */ +#define MACB_PDRSFT_SIZE 1 +#define MACB_SRI_OFFSET 26 /* TSU Seconds Register Increment */ +#define MACB_SRI_SIZE 1 + +/* Timer increment fields */ +#define MACB_TI_CNS_OFFSET 0 +#define MACB_TI_CNS_SIZE 8 +#define MACB_TI_ACNS_OFFSET 8 +#define MACB_TI_ACNS_SIZE 8 +#define MACB_TI_NIT_OFFSET 16 +#define MACB_TI_NIT_SIZE 8 /* Bitfields in MAN */ #define MACB_DATA_OFFSET 0 /* data */ @@ -386,6 +430,17 @@ #define GEM_PBUF_LSO_OFFSET 27 #define GEM_PBUF_LSO_SIZE 1 +/* Bitfields in TISUBN */ +#define GEM_SUBNSINCR_OFFSET 0 +#define GEM_SUBNSINCR_SIZE 16 + +/* Bitfields in TI */ +#define GEM_NSINCR_OFFSET 0 +#define GEM_NSINCR_SIZE 8 + +/* Bitfields in ADJ */ +#define GEM_ADDSUB_OFFSET 31 +#define GEM_ADDSUB_SIZE 1 /* Constants for CLK */ #define MACB_CLK_DIV8 0 #define MACB_CLK_DIV16 1 @@ -417,6 +472,7 @@ #define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000 #define MACB_CAPS_SG_DISABLED 0x40000000 #define MACB_CAPS_MACB_IS_GEM 0x80000000 +#define MACB_CAPS_GEM_HAS_PTP 0x00000020 /* LSO settings */ #define MACB_LSO_UFO_ENABLE 0x01 @@ -782,6 +838,19 @@ struct macb_or_gem_ops { int (*mog_rx)(struct macb *bp, int budget); }; +/* MACB-PTP interface: adapt to platform needs and GEM (e.g. GXL). */ +struct macb_ptp_info { + void (*ptp_init)(struct net_device *ndev); + void (*ptp_remove)(struct net_device *ndev); + unsigned int (*get_tsu_rate)(struct macb *bp); + int (*get_ts_info)(struct net_device *dev, + struct ethtool_ts_info *info); + int (*hwtst_get)(struct net_device *netdev, + struct ifreq *ifr); + int (*hwtst_set)(struct net_device *netdev, + struct ifreq *ifr, int cmd); +}; + struct macb_config { u32 caps; unsigned int dma_burst_length; @@ -874,11 +943,44 @@ struct macb { unsigned int jumbo_max_len; u32 wol; + + struct macb_ptp_info *ptp_info; +#ifdef CONFIG_MACB_USE_HWSTAMP + bool hwts_tx_en; + bool hwts_rx_en; + spinlock_t tsu_clk_lock; /* gem tsu clock locking */ + unsigned int tsu_rate; + + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + u32 ns_incr; + u32 subns_incr; +#endif }; +#ifdef CONFIG_MACB_USE_HWSTAMP +void gem_ptp_init(struct net_device *ndev); +void gem_ptp_remove(struct net_device *ndev); + +void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb); +void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb); + +#else +static inline void gem_ptp_init(struct net_device *ndev) { } +static inline void gem_ptp_remove(struct net_device *ndev) { } + +static inline void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) { } +static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb) { } +#endif + static inline bool macb_is_gem(struct macb *bp) { return !!(bp->caps & MACB_CAPS_MACB_IS_GEM); } +static inline bool gem_has_ptp(struct macb *bp) +{ + return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); +} + #endif /* _MACB_H */ diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c new file mode 100644 index 0000000..efefd9e --- /dev/null +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -0,0 +1,390 @@ +/* + * 1588 PTP support for GEM device. + * + * Copyright (C) 2016 Microchip Technology + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/time64.h> +#include <linux/ptp_classify.h> +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include <linux/net_tstamp.h> + +#include "macb.h" + +#define GEM_PTP_TIMER_NAME "gem-ptp-timer" + +static inline void gem_tsu_get_time(struct macb *bp, + struct timespec64 *ts) +{ + u64 sec, sech, secl; + + spin_lock(&bp->tsu_clk_lock); + + /* GEM's internal time */ + sech = gem_readl(bp, TSH); + secl = gem_readl(bp, TSL); + ts->tv_nsec = gem_readl(bp, TN); + ts->tv_sec = (sech << 32) | secl; + + /* minimize error */ + sech = gem_readl(bp, TSH); + secl = gem_readl(bp, TSL); + sec = (sech << 32) | secl; + if (ts->tv_sec != sec) { + ts->tv_sec = sec; + ts->tv_nsec = gem_readl(bp, TN); + } + + spin_unlock(&bp->tsu_clk_lock); +} + +static inline void gem_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, TN, 0); /* clear to avoid overflow */ + gem_writel(bp, TSH, sech); + gem_writel(bp, TSL, secl); + gem_writel(bp, TN, ns); + + spin_unlock(&bp->tsu_clk_lock); +} + +static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_caps); + u32 word, diff; + u64 adj, rate; + int neg_adj = 0; + + if (scaled_ppm < 0) { + neg_adj = 1; + scaled_ppm = -scaled_ppm; + } + rate = scaled_ppm; + + /* word: unused(8bit) | ns(8bit) | fractions(16bit) */ + word = (bp->ns_incr << 16) + bp->subns_incr; + + adj = word; + adj *= rate; + adj >>= 16; /* remove fractions */ + adj += 500000UL; + diff = div_u64(adj, 1000000UL); + word = neg_adj ? word - diff : word + diff; + + spin_lock(&bp->tsu_clk_lock); + + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, (word & 0xffff))); + gem_writel(bp, TI, GEM_BF(NSINCR, (word >> 16))); + + spin_unlock(&bp->tsu_clk_lock); + return 0; +} + +static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_caps); + struct timespec64 now, then = ns_to_timespec64(delta); + u32 adj, sign = 0; + + if (delta < 0) { + delta = -delta; + sign = 1; + } + + if (delta > 0x3FFFFFFF) { + gem_tsu_get_time(bp, &now); + + if (sign) + now = timespec64_sub(now, then); + else + now = timespec64_add(now, then); + + gem_tsu_set_time(bp, (const struct timespec64 *)&now); + } else { + adj = delta; + if (sign) + adj |= GEM_BIT(ADDSUB); + + gem_writel(bp, TA, adj); + } + + return 0; +} + +static int gem_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_caps); + + gem_tsu_get_time(bp, ts); + + return 0; +} + +static int gem_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_caps); + + gem_tsu_set_time(bp, ts); + + return 0; +} + +static int gem_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +static struct ptp_clock_info gem_ptp_caps_template = { + .owner = THIS_MODULE, + .name = GEM_PTP_TIMER_NAME, + .max_adj = 0, + .n_alarm = 0, + .n_ext_ts = 0, + .n_per_out = 0, + .n_pins = 0, + .pps = 0, + .adjfine = gem_ptp_adjfine, + .adjtime = gem_ptp_adjtime, + .gettime64 = gem_ptp_gettime, + .settime64 = gem_ptp_settime, + .enable = gem_ptp_enable, +}; + +static s32 gem_ptp_max_adj(unsigned int f_nom) +{ + u64 adj; + + /* The 48 bits of seconds for the GEM overflows every: + * 2^48/(365.25 * 24 * 60 *60) =~ 8 925 512 years (~= 9 mil years), + * thus the maximum adjust frequency must not overflow CNS register: + * + * addend = 10^9/nominal_freq + * adj_max = +/- addend*ppb_max/10^9 + * max_ppb = (2^8-1)*nominal_freq-10^9 + */ + adj = f_nom; + adj *= 0xffff; + adj -= 1000000000ULL; + return adj; +} + +static void gem_ptp_init_timer(struct macb *bp) +{ + struct timespec64 now; + u32 rem = 0; + + getnstimeofday64(&now); + gem_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; + + adj <<= 16; /* 16 bits nsec fragments */ + 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); +} + +static void gem_ptp_clear_timer(struct macb *bp) +{ + bp->ns_incr = 0; + bp->subns_incr = 0; + + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0)); + gem_writel(bp, TI, GEM_BF(NSINCR, 0)); + gem_writel(bp, TA, 0); +} + +/* While GEM can timestamp PTP packets, it does not mark the RX descriptor + * to identify them. UDP packets must be parsed to identify PTP packets. + * + * Note: Inspired from drivers/net/ethernet/ti/cpts.c + */ +static int gem_get_ptp_peer(struct sk_buff *skb, int ptp_class) +{ + unsigned int offset = 0; + u8 *msgtype, *data = skb->data; + + /* PTP frames are rare! */ + if (likely(ptp_class == PTP_CLASS_NONE)) + return -1; + + if (ptp_class & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (ptp_class & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; + break; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; + break; + case PTP_CLASS_L2: + offset += ETH_HLEN; + break; + + /* something went wrong! */ + default: + return -1; + } + + if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID) + return -1; + + if (unlikely(ptp_class & PTP_CLASS_V1)) + msgtype = data + offset + OFF_PTP_CONTROL; + else + msgtype = data + offset; + + return (*msgtype) & 0x2; +} + +static void gem_ptp_tx_hwtstamp(struct macb *bp, struct sk_buff *skb, + int peer_ev) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct timespec64 ts; + u64 ns; + + /* PTP Peer Event Frame packets */ + if (peer_ev) { + ts.tv_sec = gem_readl(bp, PEFTSL); + ts.tv_nsec = gem_readl(bp, PEFTN); + + /* PTP Event Frame packets */ + } else { + ts.tv_sec = gem_readl(bp, EFTSL); + ts.tv_nsec = gem_readl(bp, EFTN); + } + ns = timespec64_to_ns(&ts); + + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, skb_hwtstamps(skb)); +} + +static void gem_ptp_rx_hwtstamp(struct macb *bp, struct sk_buff *skb, + int peer_ev) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct timespec64 ts; + u64 ns; + + if (peer_ev) { + /* PTP Peer Event Frame packets */ + ts.tv_sec = gem_readl(bp, PEFRSL); + ts.tv_nsec = gem_readl(bp, PEFRN); + } else { + /* PTP Event Frame packets */ + ts.tv_sec = gem_readl(bp, EFRSL); + ts.tv_nsec = gem_readl(bp, EFRN); + } + ns = timespec64_to_ns(&ts); + + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(ns); +} + +/* no static, GEM PTP interface functions */ +void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) +{ + if (!bp->hwts_tx_en) + return; + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + int class = ptp_classify_raw(skb); + int peer; + + peer = gem_get_ptp_peer(skb, class); + if (peer < 0) + return; + + /* Timestamp this packet */ + gem_ptp_tx_hwtstamp(bp, skb, peer); + } +} + +void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb) +{ + int class, peer; + + if (!bp->hwts_rx_en) + return; + + __skb_push(skb, ETH_HLEN); + class = ptp_classify_raw(skb); + __skb_pull(skb, ETH_HLEN); + + peer = gem_get_ptp_peer(skb, class); + if (peer < 0) + return; + + gem_ptp_rx_hwtstamp(bp, skb, peer); +} + +void gem_ptp_init(struct net_device *ndev) +{ + struct macb *bp = netdev_priv(ndev); + + spin_lock_init(&bp->tsu_clk_lock); + bp->ptp_caps = gem_ptp_caps_template; + + /* nominal frequency and maximum adjustment in ppb */ + bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); + bp->ptp_caps.max_adj = gem_ptp_max_adj(bp->tsu_rate); + + gem_ptp_init_timer(bp); + + bp->ptp_clock = ptp_clock_register(&bp->ptp_caps, NULL); + 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", + GEM_PTP_TIMER_NAME); +} + +void gem_ptp_remove(struct net_device *ndev) +{ + struct macb *bp = netdev_priv(ndev); + + if (bp->ptp_clock) + ptp_clock_unregister(bp->ptp_clock); + + gem_ptp_clear_timer(bp); + + dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n", + GEM_PTP_TIMER_NAME); +}