LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* RE: [PATCH 9/9] RapidIO: Add support for IDT CPS Gen2 switches
From: Bounine, Alexandre @ 2010-08-16 18:30 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C6930EA.8030406@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > +	rio_mport_write_config_32(mport, destid, hopcount,
> > +				  LOCAL_RTE_CONF_DESTID_SEL, table);
> > +
> > +	for (i =3D 0x80000000; i <=3D 0x800000ff;) {
> > +		rio_mport_write_config_32(mport, destid, hopcount,
> > +			RIO_STD_RTE_CONF_DESTID_SEL_CSR, i);
>=20
> The 0x80000000 is that an autoincrement bit? If so, it only needs to
do
> this once I think? If so please call it like that, and loop the 'i'
> variable through the destination IDs.

No. This is EXTENDED_CONFIGURATION_ENABLE bit defined in Part 3 of RIO
spec (v.2.1, sect.3.5.4).
Anyway, I should add definition for this bit. Will do now.

Alex.
  =20

^ permalink raw reply

* RE: [PATCH 0/9] RapidIO: Set of patches to add Gen2 switches
From: Bounine, Alexandre @ 2010-08-16 18:18 UTC (permalink / raw)
  To: Micha Nelissen
  Cc: linuxppc-dev, akpm, Bounine, Alexandre, linux-kernel, Thomas Moll
In-Reply-To: <4C692F7E.1060208@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > This set of RapidIO patches adds support for new IDT Gen2 sRIO
switch
> > devices - CPS-1848 and CPS-1616.
> > Adding these sRIO switches required to implement standard error
recovery
> > mechanism defined by the RapidIO specification.
>=20
> This is not 'Gen2' specific, as these error management extensions also
> exist in v1.2/1.3 (?) of the specification? E.g. tsi56x and tsi57x
could
> support this functionality?

Correct. EM extensions exist since v1.3. But implementation before Gen2
switches relied
on proprietary device specific mechanism (tsi57x). Addition of Gen2
switches just=20
pushed forward the standard implementation. The RIO standard methods may
be used on 1.3 switches
(tested on tsi57x) as well as the standard routing table operations that
have been added
in our patches for 2.6.35.
   =20
>=20
> Micha

Alex.

^ permalink raw reply

* RE: [PATCH 7/9] RapidIO: Add handling for PW message from a lost device
From: Bounine, Alexandre @ 2010-08-16 18:02 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C692F31.2020705@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > Add check if PW message source device is accessible and change PW
message
> > handler to recover if PW message source device is not available
anymore (power
> > down or link disconnect).
>=20
> I am not quite sure what the point is of this patch. What do you need
to
> recover from?

>From failed maintenance read. In the previous version PW handler had
troubles if maintenance
read request fails. Now I am trying to detect lost or remover devices as
soon as I see broken link.

>=20
> > To avoid possible loss of notification, the PW message handler scans
the route
> > back from the source device to identify end of the broken link.
>=20
> Do you mean if port-writes are dropped? Then they did not reach you in
> the first place. If a link in between is broken, the associated switch
> will 'complain' and send port-writes, no?

Situation that I am trying to resolve is mostly applicable to larger
systems that have multiple complex boards (or chassis/domains) connected
together. Power down sequence on the board (chassis) combined with
switch hierarchy may allow switch to send PW message to the host before
its power is off. This will create an orphaned PW message.=20
At the same time there is no guarantee that PW message from the
associated switch will reach the host.
That "real" PW message may be dropped by the controller (85xx is good
example). Everything depends on number of PW messages directed to the
host/controller. I am trying to use the first available notification to
service device removal. If the "real" PW message is received it should
be processed without any further action.=20

Alex.

^ permalink raw reply

* RE: [PATCH 6/9] RapidIO: Add switch-specific sysfs initialization callback
From: Bounine, Alexandre @ 2010-08-16 17:10 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C692E4C.4040300@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > -	if (!rdev->rswitch)
> > -		goto out;
> > -
>=20
> Is it safe? All devices have a switch?

Yes. Because end-points should not have the "routes" attribute at all
(corrected by this patch).

>=20
> > @@ -63,10 +59,11 @@ struct device_attribute rio_dev_attrs[] =3D {
> >  	__ATTR_RO(asm_did),
> >  	__ATTR_RO(asm_vid),
> >  	__ATTR_RO(asm_rev),
> > -	__ATTR_RO(routes),
> >  	__ATTR_NULL,
> >  };
> >
> > +static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
> > +
>=20
> This seems a separate change from the sw_sysfs? Why make it separate?

I assume that your question was "Why do not make it separate?"
Both changes are specific to switches, both address sysfs and both are
not big enough to justify
a separate patch.
I agree that make separate patches would give more clarity, so would do
better description.
Because there are changes that should be made to other patches in this
set, I will regenerate this patch with better description.

>=20
> >   */
> >  struct rio_switch {
> > @@ -256,6 +257,7 @@ struct rio_switch {
> >  			   u8 *sw_domain);
> >  	int (*em_init) (struct rio_dev *dev);
> >  	int (*em_handle) (struct rio_dev *dev, u8 swport);
> > +	int (*sw_sysfs) (struct rio_dev *dev, int create);
> >  	struct rio_dev *nextdev[0];
> >  };
>=20
> Why not make a sw_sysfs_create and sw_sysfs_remove? Is better for
> readability. Now you call 'sw_sysfs(dev, 0)' or 'sw_sysfs(dev, 1)';

I just do not want to have an extra member here. Not every switch will
require own sysfs attributes, but every switch will be presented by a
data structure. Based on its intended use I do not see any problem here.

^ permalink raw reply

* Re: Linuxppc-dev Digest, Vol 72, Issue 63
From: Manikandan Ramachandran @ 2010-08-16 17:01 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <mailman.5.1281664801.6486.linuxppc-dev@lists.ozlabs.org>

[-- Attachment #1: Type: text/plain, Size: 2052 bytes --]

> Date: Thu, 12 Aug 2010 13:53:51 -0400
> From: Jeff Angielski <jeff@theptrgroup.com>
> To: linuxppc-dev@lists.ozlabs.org
> Subject: Re: Query regarding 2.6.335 RT[Ingo's] and Non-RT performance
> Message-ID: <4C64352F.4090005@theptrgroup.com>
> Content-Type: text/plain; charset=ISO-8859-1; format=flowed
>
> On 08/11/2010 06:18 PM, Manikandan Ramachandran wrote:
> > Hello All,
> >      I created a very simple program which has higher priority than
> > normal tasks and runs a tight loop. Under same test environment I ran
> > this program on both non-rt and rt 2.6.33.5 kernel.  To my suprise I see
> > that performance of non-RT kernel is better than RT. non-RT kernel took
> > 3 sec and 366156 usec while RT kernel took about 3 sec and 418011
> > usec.Can someone please explain why the performance of non-rt kernel is
> > better than rt kernel? From the face of the test result, I feel RT has
> > more overhead,Is there any configuration that I could do to bring down
> > the overhead?
>
> Your "surprise" is due to your definition of "performance".
>
> The purpose of the -rt kernels is to reduce the kernel latency.  This is
> important for servicing hardware.  Normal users find the -rt useful for
> audio/video applications.  Engineering and scientific users find the -rt
> beneficially for servicing hardware like sensors or control systems.
>
> If you are just trying to run calculations as fast as you can in user
> space, you'd be better off using the non-rt variants.
>
>
> --
> Jeff Angielski
> The PTR Group
> www.theptrgroup.com
>
>
> Thanks for your response.
>
> On one hand I hear that RT-kernel is meant for reducing kernel latency on
> other hand I see that there is RT-kernel overhead. So what really RT-kernel
> brings to system performance?
>
> Actually I see that latency for higher priority is more or less same for
> non-rt system.
>
> One more thing, since irqs being threaded in RT, and with CFS scheduler in
> 2.6.33, wouldn't we bring down system performance as CFS is O(log(n))  in
> nature?
> --
> Thanks,
> Manik
>

[-- Attachment #2: Type: text/html, Size: 2639 bytes --]

^ permalink raw reply

* RE: [PATCH 2/9] RapidIO, powerpc/85xx: modify RIO port-write interrupt handler
From: Bounine, Alexandre @ 2010-08-16 15:27 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C692B42.9050407@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > - Rearranged RIO port-write interrupt handling to perform message
buffering
> > as soon as possible.
>=20
> I don't understand this comment: you still schedule work to read the
> port-write queue; so how is this message buffering performed as soon
as
> possible?


Compared to the original code, I rearranged order of checking interrupt
status bits to check the queue status first. The 85xx PW controller is
capable to receive and keep only one PW message. Therefore, I copy it
into the driver's FIFO and re-enable HW Rx queue (it is called queue but
can accept only one entry) ASAP. I have a test setup that is capable
generate multiple PW messages and see many messages discarded by PW
controller
because of this single-entry HW queue.
=20
>=20
> > - Modified to disable port-write controller when clearing
Transaction Error (TE)
> > bit.
> >  	/* Schedule deferred processing if PW was received */
> > -	if (ipwsr & RIO_IPWSR_QFI) {
> > +	if ((ipwmr & RIO_IPWMR_QFIE) && (ipwsr & RIO_IPWSR_QFI)) {
>=20
> Why check the QFIE bit also?

Oops! Leftover from some testing. Will clean it up.

>=20
> > +pw_done:
> > +	if (epwisr & 0x80000000) {
>=20
> Magic value.


Agree. Will correct.

^ permalink raw reply

* Re: Distinguish between kernel and user space
From: Ira W. Snyder @ 2010-08-16 15:22 UTC (permalink / raw)
  To: Ravi Gupta; +Cc: linuxppc-dev, MJ embd, linuxppc-dev
In-Reply-To: <AANLkTi=ZSeYf0apwK3Tuf3b3-OTsRrddMkNHkxgViw-x@mail.gmail.com>

On Mon, Aug 16, 2010 at 05:52:41PM +0530, Ravi Gupta wrote:
> Hi,
> 
> I have defined a header file for ioctls macros definitions. I am including
> it in both, my user space application as well as in my device driver. Now
> there are some macros that I want to be visible only in device drive and
> some only in user space application. Is there any set of macros defined in
> linux for such purpose? This is my present header file
> 
> gpio_ioctl.h
> 
> #ifndef _GPIO_IOCTL_H_
> #define _GPIO_IOCTL_H_
> 
> /*
>  * Ioctl definitions
>  */
> 
> /* Use 250 as type/magic number */
> #define GPIO_IOC_MAGIC    250
> #define GPIO_READ         _IOW (GPIO_IOC_MAGIC, 0, int)
> #define GPIO_WRITE        _IOW (GPIO_IOC_MAGIC, 1, int)
> #define GPIO_IOC_MAXNR    2
> 
> 
> #ifdef _DEVICE_DRIVE_ ---> these macros should not be visible in user space
> application
> #define ...
> ...
> ..
> #endif
> 
> #ifdef _USER_SPACE_ ---> these macros should not be visible in device driver
> i.e kernel space
> #define ...
> ...
> ..
> 
> #endif  /*_GPIO_IOCTL_H_ */
> 

#ifdef __KERNEL__
/* kernel only definitions here */
#else
/* userspace only definitions here */
#endif

See tons of headers in include/linux/ for examples.

Ira

^ permalink raw reply

* RE: [PATCH 5/9] RapidIO: Add default handler for error_stopped state
From: Bounine, Alexandre @ 2010-08-16 14:47 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C692D4D.1030401@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > +
> > +	if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) {
> > +		pr_debug("RIO_EM: servicing Output Error-Stopped
state\n");
> > +		/*
> > +		 * Send a Link-Request/Input-Status control symbol
> > +		 */
> > +
> > +		/* Read from link maintenance response register
> > +		 * to clear valid bit */
> > +		rio_mport_read_config_32(mport, destid, hopcount,
> > +			rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
> > +			&regval);
> > +		udelay(50);
>=20
> Perhaps this whole part of operating the MNT_RSP registers (sending
> link-request symbol is the only useful action IIRC?) can be put in a
> separate function for readability.
>=20
I was on the fence with this one. Now I have an extra vote in favor of a
separate function ;)
Will do.
=20

^ permalink raw reply

* Re: [PATCH 0/8] v5 De-couple sysfs memory directories from memory sections
From: Nathan Fontenot @ 2010-08-16 14:34 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linuxppc-dev, Greg KH, linux-kernel, Dave Hansen, linux-mm,
	KAMEZAWA Hiroyuki
In-Reply-To: <20100812120816.e97d8b9e.akpm@linux-foundation.org>

On 08/12/2010 02:08 PM, Andrew Morton wrote:
> On Mon, 09 Aug 2010 12:53:00 -0500
> Nathan Fontenot <nfont@austin.ibm.com> wrote:
> 
>> This set of patches de-couples the idea that there is a single
>> directory in sysfs for each memory section.  The intent of the
>> patches is to reduce the number of sysfs directories created to
>> resolve a boot-time performance issue.  On very large systems
>> boot time are getting very long (as seen on powerpc hardware)
>> due to the enormous number of sysfs directories being created.
>> On a system with 1 TB of memory we create ~63,000 directories.
>> For even larger systems boot times are being measured in hours.
> 
> And those "hours" are mainly due to this problem, I assume.

Yes, those hours are spent creating the sysfs directories for each
of the memory sections.

> 
>> This set of patches allows for each directory created in sysfs
>> to cover more than one memory section.  The default behavior for
>> sysfs directory creation is the same, in that each directory
>> represents a single memory section.  A new file 'end_phys_index'
>> in each directory contains the physical_id of the last memory
>> section covered by the directory so that users can easily
>> determine the memory section range of a directory.
> 
> What you're proposing appears to be a non-back-compatible
> userspace-visible change.  This is a big issue!
> 
> It's not an unresolvable issue, as this is a must-fix problem.  But you
> should tell us what your proposal is to prevent breakage of existing
> installations.  A Kconfig option would be good, but a boot-time kernel
> command line option which selects the new format would be much better.

This shouldn't break existing installations, unless an architecture chooses
to do so.  With my patch only the powerpc/pseries arch is updated such that
what is seen in userspace is different.

The default behavior is maintained for all architectures unless they define
their own version of memory_block_size_bytes().  The default definition of
this routine (defined as __weak in Patch 5/8) sets the memory block size
to the same size it currently is, and thus preserving the exisitng 1 sysfs
directory per memory section.  The only change that will be seen is a new
propery for memory section, end_phys_addr, which will have the same value
as the existing 'phys_addr' property.

> 
> However you didn't mention this issue at all, and it's the most
> important one.
> 
> 
>> Updates for version 5 of the patchset include the following:
>>
>> Patch 4/8 Add mutex for add/remove of memory blocks
>> - Define the mutex using DEFINE_MUTEX macro.
>>
>> Patch 8/8 Update memory-hotplug documentation
>> - Add information concerning memory holes in phys_index..end_phys_index.
> 
> And you forgot to tell us how long those machines boot with the
> patchset applied, which is the entire point of the patchset!

Yes,  I am working on getting more time on our large systems to get
performance numbers with this patch.  I'll post them when I get them.

-Nathan

^ permalink raw reply

* Re: [PATCH 1/5] ptp: Added a brand new class driver for ptp clocks.
From: Arnd Bergmann @ 2010-08-16 14:26 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Rodolfo Giometti, netdev, devicetree-discuss, linux-kernel,
	linuxppc-dev, linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <363bd749a38d0b785d8431e591bf54c38db4c2d7.1281956490.git.richard.cochran@omicron.at>

On Monday 16 August 2010, Richard Cochran wrote:
> This patch adds an infrastructure for hardware clocks that implement
> IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a
> registration method to particular hardware clock drivers. Each clock is
> exposed to user space as a character device with ioctls that allow tuning
> of the PTP clock.

Have you considered integrating the subsystem into the Posix clock/timer
framework?

I can't really tell from reading the source if this is possible or
not, but my feeling is that if it can be done, that would be a much
nicer interface. We already have clock_gettime/clock_settime/
timer_settime/... system calls, and while you'd need to add another
clockid and some syscalls, my feeling is that it will be more
usable in the end.

> +static const struct file_operations ptp_fops = {
> +       .owner          = THIS_MODULE,
> +       .ioctl          = ptp_ioctl,
> +       .open           = ptp_open,
> +       .poll           = ptp_poll,
> +       .read           = ptp_read,
> +       .release        = ptp_release,
> +};

.ioctl is gone, you have to use .unlocked_ioctl and make sure that
your ptp_ioctl function can handle being called concurrently.

You should also add a .compat_ioctl function, ideally one that
points to ptp_ioctl so you don't have to list every command as
being compatible. Right now, some commands are incompatible,
which means you need more changes to the interface.

> +struct ptp_clock_timer {
> +       int alarm_index;       /* Which alarm to query or configure. */
> +       int signum;            /* Requested signal. */
> +       int flags;             /* Zero or TIMER_ABSTIME, see TIMER_SETTIME(2) */
> +       struct itimerspec tsp; /* See TIMER_SETTIME(2) */
> +};

This data structure is incompatible between 32 and 64 bit user space,
so you would need a compat_ioctl() function to convert between the
two formats. Better define the structure in a compatible way, avoiding
variable-length fields and implicit padding.

> +struct ptp_clock_request {
> +       int type;  /* One of the ptp_request_types enumeration values. */
> +       int index; /* Which channel to configure. */
> +       struct timespec ts; /* For period signals, the desired period. */
> +       int flags; /* Bit field for PTP_ENABLE_FEATURE or other flags. */
> +};

Same problem here, timespec is either 64 or 128 bits long and has different
alignment.

> +struct ptp_extts_event {
> +       int index;
> +       struct timespec ts;
> +};

here too.

> +#define PTP_CLOCK_VERSION 0x00000001
> +
> +#define PTP_CLK_MAGIC '='
> +
> +#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)

Try avoiding versioned ABIs. It's better to just add new ioctls
or syscalls when you need some extra functionality and leave the
old ones around.

> +#define PTP_CLOCK_ADJTIME _IOW (PTP_CLK_MAGIC, 3, struct timespec)
> +#define PTP_CLOCK_GETTIME _IOR (PTP_CLK_MAGIC, 4, struct timespec)
> +#define PTP_CLOCK_SETTIME _IOW (PTP_CLK_MAGIC, 5, struct timespec)

These are also incompatible.

	Arnd

^ permalink raw reply

* RE: [PATCH 4/9] RapidIO: Add relation links between RIO device structures
From: Bounine, Alexandre @ 2010-08-16 14:00 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel, Bounine, Alexandre
In-Reply-To: <4C692C9D.6040100@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > Create back and forward links between RIO devices. These links are
intended for
> > use by error management and hot-plug extensions.
>=20
> As RapidIO is a switched network, the concept of 'previous' and 'next'
> devices is invalid. Perhaps it's just the way they were
> discovered/enumerated, but that does not matter any more at runtime.
Or
> at least, should not matter.
>

Yes, the "previous" and "next" have to be considered in context of
enumeration/discovery.
At runtime, it does not matter for data traffic, but is valuable
information for error recovery
And hot-swap. This provides snapshot of device connections. For example,
when servicing
error-stopped state of link between two switches you need to know
ingress port of far switch on
the link. It may be a problem to read it directly from the device.
Searching through the device list
becomes time consuming as a system size grows.     =20
=20
> > @@ -237,6 +241,7 @@ struct rio_switch {
> >  	u16 hopcount;
> >  	u16 destid;
> >  	u8  inport;
> > +	u8  nports;
> >  	u8 *route_table;
> >  	u32 port_ok;
>=20
> This can be extracted from the swpinfo field (which you removed in
> another patch)?
>=20
Agreed in response for patch #3

^ permalink raw reply

* RE: [PATCH 3/9] RapidIO: Add the ingress port number into the RIO switch data structure
From: Bounine, Alexandre @ 2010-08-16 13:40 UTC (permalink / raw)
  To: Micha Nelissen; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <4C692BBB.8090707@neli.hopto.org>

Micha Nelissen wrote:
>=20
> Alexandre Bounine wrote:
> > A switch ingress port number has to be saved for software assisted
error
> > recovery from the error-stopped state. Saving this information also
allows
> > to remove several register reads from the RIO enumeration process.
>=20
> Why not keep using the swpinfo field, as you can extract it from
there?
> And your code actually does this.
>=20
> Also processor devices can have multiple ports (not only switches), it
> would be good to be ready for that use case.
>=20
Agree. I missed the Multiport bit introduced in RIO spec 2.1. Now it is
justified to be as it was before.
I will rework this and next patches.=20

^ permalink raw reply

* Re: [PATCH 00/27] KVM PPC PV framework v3
From: Alexander Graf @ 2010-08-16 13:11 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, KVM list, kvm-ppc
In-Reply-To: <17DF4824-9EC7-44F4-9408-EA6241C33E70@kernel.crashing.org>


On 06.08.2010, at 18:28, Kumar Gala wrote:

>=20
> On Jul 29, 2010, at 7:47 AM, Alexander Graf wrote:
>=20
>> [without]
>>=20
>> debian-powerpc:~# time for i in {1..1000}; do /bin/echo hello > =
/dev/null; done
>>=20
>> real    0m14.659s
>> user    0m8.967s
>> sys     0m5.688s
>>=20
>> [with]
>>=20
>> debian-powerpc:~# time for i in {1..1000}; do /bin/echo hello > =
/dev/null; done
>>=20
>> real    0m7.557s
>> user    0m4.121s
>> sys     0m3.426s
>=20
> Do you have #'s for w/o HV to compare to.

Native was around 2-3 seconds overall. So we still have quite a big =
impact on performance, but things are improving :).

Alex

^ permalink raw reply

* Re: [PATCH 9/9] RapidIO: Add support for IDT CPS Gen2 switches
From: Micha Nelissen @ 2010-08-16 12:36 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-10-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> +	rio_mport_write_config_32(mport, destid, hopcount,
> +				  LOCAL_RTE_CONF_DESTID_SEL, table);
> +
> +	for (i = 0x80000000; i <= 0x800000ff;) {
> +		rio_mport_write_config_32(mport, destid, hopcount,
> +			RIO_STD_RTE_CONF_DESTID_SEL_CSR, i);

The 0x80000000 is that an autoincrement bit? If so, it only needs to do 
this once I think? If so please call it like that, and loop the 'i' 
variable through the destination IDs.

Micha

^ permalink raw reply

* Re: [PATCH 0/9] RapidIO: Set of patches to add Gen2 switches
From: Micha Nelissen @ 2010-08-16 12:30 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel, Thomas Moll
In-Reply-To: <1281712686-31308-1-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> This set of RapidIO patches adds support for new IDT Gen2 sRIO switch
> devices - CPS-1848 and CPS-1616.
> Adding these sRIO switches required to implement standard error recovery
> mechanism defined by the RapidIO specification.

This is not 'Gen2' specific, as these error management extensions also 
exist in v1.2/1.3 (?) of the specification? E.g. tsi56x and tsi57x could 
support this functionality?

Micha

^ permalink raw reply

* Re: [PATCH 7/9] RapidIO: Add handling for PW message from a lost device
From: Micha Nelissen @ 2010-08-16 12:29 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-8-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> Add check if PW message source device is accessible and change PW message
> handler to recover if PW message source device is not available anymore (power
> down or link disconnect).

I am not quite sure what the point is of this patch. What do you need to 
recover from?

> To avoid possible loss of notification, the PW message handler scans the route
> back from the source device to identify end of the broken link.

Do you mean if port-writes are dropped? Then they did not reach you in 
the first place. If a link in between is broken, the associated switch 
will 'complain' and send port-writes, no?

Micha

^ permalink raw reply

* Re: [PATCH 6/9] RapidIO: Add switch-specific sysfs initialization callback
From: Micha Nelissen @ 2010-08-16 12:25 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-7-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> -	if (!rdev->rswitch)
> -		goto out;
> -

Is it safe? All devices have a switch?

> @@ -63,10 +59,11 @@ struct device_attribute rio_dev_attrs[] = {
>  	__ATTR_RO(asm_did),
>  	__ATTR_RO(asm_vid),
>  	__ATTR_RO(asm_rev),
> -	__ATTR_RO(routes),
>  	__ATTR_NULL,
>  };
>  
> +static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
> +

This seems a separate change from the sw_sysfs? Why make it separate?

>   */
>  struct rio_switch {
> @@ -256,6 +257,7 @@ struct rio_switch {
>  			   u8 *sw_domain);
>  	int (*em_init) (struct rio_dev *dev);
>  	int (*em_handle) (struct rio_dev *dev, u8 swport);
> +	int (*sw_sysfs) (struct rio_dev *dev, int create);
>  	struct rio_dev *nextdev[0];
>  };

Why not make a sw_sysfs_create and sw_sysfs_remove? Is better for 
readability. Now you call 'sw_sysfs(dev, 0)' or 'sw_sysfs(dev, 1)';

Micha

^ permalink raw reply

* Distinguish between kernel and user space
From: Ravi Gupta @ 2010-08-16 12:22 UTC (permalink / raw)
  To: linuxppc-dev, linuxppc-dev, Ira W. Snyder, MJ embd,
	Anton Vorontsov

[-- Attachment #1: Type: text/plain, Size: 942 bytes --]

Hi,

I have defined a header file for ioctls macros definitions. I am including
it in both, my user space application as well as in my device driver. Now
there are some macros that I want to be visible only in device drive and
some only in user space application. Is there any set of macros defined in
linux for such purpose? This is my present header file

gpio_ioctl.h

#ifndef _GPIO_IOCTL_H_
#define _GPIO_IOCTL_H_

/*
 * Ioctl definitions
 */

/* Use 250 as type/magic number */
#define GPIO_IOC_MAGIC    250
#define GPIO_READ         _IOW (GPIO_IOC_MAGIC, 0, int)
#define GPIO_WRITE        _IOW (GPIO_IOC_MAGIC, 1, int)
#define GPIO_IOC_MAXNR    2


#ifdef _DEVICE_DRIVE_ ---> these macros should not be visible in user space
application
#define ...
...
..
#endif

#ifdef _USER_SPACE_ ---> these macros should not be visible in device driver
i.e kernel space
#define ...
...
..

#endif  /*_GPIO_IOCTL_H_ */

Thanks in advance
Ravi Gupta

[-- Attachment #2: Type: text/html, Size: 1073 bytes --]

^ permalink raw reply

* Re: [PATCH 2/9] RapidIO, powerpc/85xx: modify RIO port-write interrupt handler
From: Micha Nelissen @ 2010-08-16 12:12 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-3-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> - Rearranged RIO port-write interrupt handling to perform message buffering
> as soon as possible.

I don't understand this comment: you still schedule work to read the 
port-write queue; so how is this message buffering performed as soon as 
possible?

> - Modified to disable port-write controller when clearing Transaction Error (TE)
> bit.
>  	/* Schedule deferred processing if PW was received */
> -	if (ipwsr & RIO_IPWSR_QFI) {
> +	if ((ipwmr & RIO_IPWMR_QFIE) && (ipwsr & RIO_IPWSR_QFI)) {

Why check the QFIE bit also?

> +pw_done:
> +	if (epwisr & 0x80000000) {

Magic value.

Micha

^ permalink raw reply

* Re: [PATCH 3/9] RapidIO: Add the ingress port number into the RIO switch  data structure
From: Micha Nelissen @ 2010-08-16 12:14 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-4-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> A switch ingress port number has to be saved for software assisted error
> recovery from the error-stopped state. Saving this information also allows
> to remove several register reads from the RIO enumeration process.

Why not keep using the swpinfo field, as you can extract it from there? 
And your code actually does this.

Also processor devices can have multiple ports (not only switches), it 
would be good to be ready for that use case.

Micha

^ permalink raw reply

* Re: [PATCH 5/9] RapidIO: Add default handler for error_stopped state
From: Micha Nelissen @ 2010-08-16 12:21 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-6-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> +
> +	if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) {
> +		pr_debug("RIO_EM: servicing Output Error-Stopped state\n");
> +		/*
> +		 * Send a Link-Request/Input-Status control symbol
> +		 */
> +
> +		/* Read from link maintenance response register
> +		 * to clear valid bit */
> +		rio_mport_read_config_32(mport, destid, hopcount,
> +			rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
> +			&regval);
> +		udelay(50);

Perhaps this whole part of operating the MNT_RSP registers (sending 
link-request symbol is the only useful action IIRC?) can be put in a 
separate function for readability.

Micha

^ permalink raw reply

* Re: [PATCH 4/9] RapidIO: Add relation links between RIO device structures
From: Micha Nelissen @ 2010-08-16 12:18 UTC (permalink / raw)
  To: Alexandre Bounine; +Cc: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <1281712686-31308-5-git-send-email-alexandre.bounine@idt.com>

Alexandre Bounine wrote:
> Create back and forward links between RIO devices. These links are intended for
> use by error management and hot-plug extensions.

As RapidIO is a switched network, the concept of 'previous' and 'next' 
devices is invalid. Perhaps it's just the way they were 
discovered/enumerated, but that does not matter any more at runtime. Or 
at least, should not matter.

> @@ -237,6 +241,7 @@ struct rio_switch {
>  	u16 hopcount;
>  	u16 destid;
>  	u8  inport;
> +	u8  nports;
>  	u8 *route_table;
>  	u32 port_ok;

This can be extracted from the swpinfo field (which you removed in 
another patch)?

Micha

^ permalink raw reply

* [PATCH 5/5] ptp: Added a clock driver for the National Semiconductor PHYTER.
From: Richard Cochran @ 2010-08-16 11:19 UTC (permalink / raw)
  To: netdev
  Cc: linuxppc-dev, devicetree-discuss, linux-kernel, Rodolfo Giometti,
	linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <cover.1281956490.git.richard.cochran@omicron.at>

This patch adds support for the PTP clock found on the DP83640.
The basic clock operations and one external time stamp have
been implemented.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 drivers/net/phy/Kconfig       |   29 ++
 drivers/net/phy/Makefile      |    1 +
 drivers/net/phy/dp83640.c     |  904 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/dp83640_reg.h |  261 ++++++++++++
 4 files changed, 1195 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/phy/dp83640.c
 create mode 100644 drivers/net/phy/dp83640_reg.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index a527e37..a2d0753 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -77,6 +77,35 @@ config NATIONAL_PHY
 	---help---
 	  Currently supports the DP83865 PHY.
 
+config DP83640_PHY
+	tristate "Driver for the National Semiconductor DP83640 PHYTER"
+	depends on PTP_1588_CLOCK
+	depends on NETWORK_PHY_TIMESTAMPING
+	---help---
+	  Supports the DP83640 PHYTER with IEEE 1588 features.
+
+	  This driver adds support for using the DP83640 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.
+
+	  In order for this to work, your MAC driver must also
+	  implement the skb_tx_timetamp() function.
+
+config DP83640_PHY_STATUS_FRAMES
+	bool "DP83640 Status Frames"
+	default y
+	depends on DP83640_PHY
+	---help---
+	  This option allows the DP83640 PHYTER driver to obtain time
+	  stamps from the PHY via special status frames, rather than
+	  reading over the MDIO bus. Using status frames is therefore
+	  more efficient. However, if enabled, this option will cause
+	  the driver to add a mutlicast address to the MAC.
+
+	  Say Y here, unless your MAC does not support multicast
+	  destination addresses.
+
 config STE10XP
 	depends on PHYLIB
 	tristate "Driver for STMicroelectronics STe10Xp PHYs"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 13bebab..2333215 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_FIXED_PHY)		+= fixed.o
 obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o
 obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o
 obj-$(CONFIG_NATIONAL_PHY)	+= national.o
+obj-$(CONFIG_DP83640_PHY)	+= dp83640.o
 obj-$(CONFIG_STE10XP)		+= ste10Xp.o
 obj-$(CONFIG_MICREL_PHY)	+= micrel.o
 obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
new file mode 100644
index 0000000..38a7202
--- /dev/null
+++ b/drivers/net/phy/dp83640.c
@@ -0,0 +1,904 @@
+/*
+ * Driver for the National Semiconductor DP83640 PHYTER
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/net_tstamp.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "dp83640_reg.h"
+
+#ifdef CONFIG_DP83640_PHY_STATUS_FRAMES
+#define USE_STATUS_FRAMES
+#endif
+
+#define DP83640_PHY_ID	0x20005ce1
+#define PAGESEL		0x13
+#define LAYER4		0x02
+#define LAYER2		0x01
+#define MAX_RXTS	4
+#define MAX_TXTS	4
+#define N_EXT_TS	1
+#define PSF_PTPVER	2
+#define PSF_EVNT	0x4000
+#define PSF_RX		0x2000
+#define PSF_TX		0x1000
+#define EXT_EVENT	1
+#define EXT_GPIO	1
+
+#if defined(__BIG_ENDIAN)
+#define ENDIAN_FLAG	0
+#elif defined(__LITTLE_ENDIAN)
+#define ENDIAN_FLAG	PSF_ENDIAN
+#endif
+
+#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
+
+struct phy_rxts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+	u16 seqid;   /* sequenceId[15:0] */
+	u16 msgtype; /* messageType[3:0], hash[11:0] */
+};
+
+struct phy_txts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+};
+
+struct rxts {
+	struct list_head list;
+	unsigned long tmo;
+	u64 ns;
+	u16 seqid;
+	u8  msgtype;
+	u16 hash;
+};
+
+struct dp83640_private {
+	struct phy_device *phydev;
+	struct work_struct ts_work;
+	int hwts_tx_en;
+	int hwts_rx_en;
+	int layer;
+	int version;
+	/* protects extended registers from concurrent access */
+	struct mutex extreg_mux;
+	int page;
+	/* remember the last event time stamp */
+	struct phy_txts edata;
+	/* list of rx timestamps */
+	struct list_head rxts;
+	struct list_head rxpool;
+	struct rxts rx_pool_data[MAX_RXTS];
+	/* protects above three fields from concurrent access */
+	spinlock_t rx_lock;
+
+	struct sk_buff_head rx_queue;
+	struct sk_buff_head tx_queue;
+};
+
+/* globals */
+
+static struct ptp_clock *dp83640_clock;
+DEFINE_MUTEX(clock_lock); /* protects the one and only dp83640_clock */
+
+static void do_timestamp_work(struct work_struct *work);
+
+/* extended register access functions */
+
+static int ext_read(struct phy_device *phydev, int page, u32 regnum)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	int val;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	val = phy_read(phydev, regnum);
+
+	return val;
+}
+
+static void ext_write(struct phy_device *phydev, int page, u32 regnum, u16 val)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	phy_write(phydev, regnum, val);
+}
+
+static int tdr_write(struct phy_device *phydev, struct timespec *ts, u16 cmd)
+{
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec >> 16);   /* ns[31:16] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec >> 16);    /* sec[31:16] */
+
+	ext_write(phydev, PAGE4, PTP_CTL, cmd);
+
+	return 0;
+}
+
+/* convert phy timestamps into driver timestamps */
+
+static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
+{
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	rxts->ns = p->ns_lo;
+	rxts->ns |= (p->ns_hi & 0x3fff) << 16;
+	rxts->ns += ((u64)sec) * 1000000000ULL;
+	rxts->seqid = p->seqid;
+	rxts->msgtype = (p->msgtype >> 12) & 0xf;
+	rxts->hash = p->msgtype & 0x0fff;
+}
+
+static u64 phy2txts(struct phy_txts *p)
+{
+	u64 ns;
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	ns = p->ns_lo;
+	ns |= (p->ns_hi & 0x3fff) << 16;
+	ns += ((u64)sec) * 1000000000ULL;
+
+	return ns;
+}
+
+/* ptp clock methods */
+
+static int ptp_dp83640_adjfreq(void *priv, s32 ppb)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	u64 rate;
+	int neg_adj = 0;
+	u16 hi, lo;
+
+	if (!ppb)
+		return 0;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	rate = ppb;
+	rate <<= 26;
+	rate = div_u64(rate, 1953125);
+
+	hi = (rate >> 16) & PTP_RATE_HI_MASK;
+	if (neg_adj)
+		hi |= PTP_RATE_DIR;
+
+	lo = rate & 0xffff;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_RATEH, hi);
+	ext_write(phydev, PAGE4, PTP_RATEL, lo);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return 0;
+}
+
+static int ptp_dp83640_adjtime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	int err;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, ts, PTP_STEP_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_gettime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	unsigned int val[4];
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_CTL, PTP_RD_CLK);
+
+	val[0] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */
+	val[1] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] */
+	val[2] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] */
+	val[3] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] */
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	ts->tv_nsec = val[0] | (val[1] << 16);
+	ts->tv_sec  = val[2] | (val[3] << 16);
+
+	return 0;
+}
+
+static int ptp_dp83640_settime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	int err;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, ts, PTP_LOAD_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+	/* We do not offer any ancillary timer features. */
+	return -EOPNOTSUPP;
+}
+
+static int ptp_dp83640_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+	/* We do not offer any ancillary timer features. */
+	return -EOPNOTSUPP;
+}
+
+static int ptp_dp83640_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	u16 evnt;
+
+	switch (rq->type) {
+	case PTP_REQUEST_EXTTS:
+		if (rq->index != 0)
+			return -EINVAL;
+		evnt = EVNT_WR | (EXT_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+		if (on) {
+			evnt |= (EXT_GPIO & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+			evnt |= EVNT_RISE;
+		}
+		ext_write(phydev, PAGE5, PTP_EVNT, evnt);
+		return 0;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_dp83640_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "dp83640 timer",
+	.max_adj	= 1953124,
+	.n_alarm	= 0,
+	.n_ext_ts	= N_EXT_TS,
+	.n_per_out	= 0,
+	.pps		= 0,
+	.priv		= NULL,
+	.adjfreq	= ptp_dp83640_adjfreq,
+	.adjtime	= ptp_dp83640_adjtime,
+	.gettime	= ptp_dp83640_gettime,
+	.settime	= ptp_dp83640_settime,
+	.gettimer	= ptp_dp83640_gettimer,
+	.settimer	= ptp_dp83640_settimer,
+	.enable		= ptp_dp83640_enable,
+};
+
+/* status frame conditional code */
+
+#ifdef USE_STATUS_FRAMES
+
+static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
+static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+	u16 cfg0 = 0, ver;
+
+	if (on)
+		cfg0 = PSF_EVNT_EN | PSF_RXTS_EN | PSF_TXTS_EN | ENDIAN_FLAG;
+
+	ver = (PSF_PTPVER & VERSIONPTP_MASK) << VERSIONPTP_SHIFT;
+
+	ext_write(phydev, PAGE5, PSF_CFG0, cfg0);
+	ext_write(phydev, PAGE6, PSF_CFG1, ver);
+
+	if (!phydev->attached_dev) {
+		pr_err("expected to find an attached netdevice\n");
+		BUG();
+	}
+
+	if (on) {
+		if (dev_mc_add(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to add mc address\n");
+	} else {
+		if (dev_mc_del(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to delete mc address\n");
+	}
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	struct ethhdr *h = eth_hdr(skb);
+
+	if (PTP_CLASS_V2_L2 == type &&
+	    !memcmp(h->h_source, status_frame_src, sizeof(status_frame_src)))
+		return true;
+	else
+		return false;
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+}
+
+#else /* USE_STATUS_FRAMES */
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	return false;
+}
+
+static void read_rxts(struct phy_device *phydev, struct rxts *rxts)
+{
+	struct phy_rxts p;
+
+	p.ns_lo   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.ns_hi   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_lo  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_hi  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.seqid   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.msgtype = ext_read(phydev, PAGE4, PTP_RXTS);
+
+	rxts->tmo = jiffies + HZ;
+	phy2rxts(&p, rxts);
+}
+
+static u64 read_txts(struct phy_device *phydev)
+{
+	struct phy_txts p;
+
+	p.ns_lo  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.ns_hi  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_lo = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_hi = ext_read(phydev, PAGE4, PTP_TXTS);
+
+	return phy2txts(&p);
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+	struct rxts *rxts;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	for (;;) {
+		int val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & RXTS_RDY))
+			break;
+		if (list_empty(&dp83640->rxpool)) {
+			pr_warning("dp83640: rx timestamp pool is empty\n");
+			break;
+		}
+		rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+		list_del_init(&rxts->list);
+		read_rxts(dp83640->phydev, rxts);
+		list_add_tail(&rxts->list, &dp83640->rxts);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+	int val;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	while ((skb = skb_dequeue(&dp83640->tx_queue)) != NULL) {
+
+		val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & TXTS_RDY)) {
+			skb_queue_head(&dp83640->tx_queue, skb);
+			break;
+		}
+		ns = read_txts(dp83640->phydev);
+		memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+		shhwtstamps.hwtstamp = ns_to_ktime(ns);
+		skb_complete_tx_timestamp(skb, &shhwtstamps);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+#endif /* !USE_STATUS_FRAMES */
+
+/* time stamping methods */
+
+static void decode_evnt(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts, u16 ests)
+{
+	struct ptp_clock_event event;
+	int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
+
+	switch (words) { /* fall through in every case */
+	case 3:
+		dp83640->edata.sec_hi = phy_txts->sec_hi;
+	case 2:
+		dp83640->edata.sec_lo = phy_txts->sec_lo;
+	case 1:
+		dp83640->edata.ns_hi = phy_txts->ns_hi;
+	case 0:
+		dp83640->edata.ns_lo = phy_txts->ns_lo;
+	}
+
+	event.type = PTP_CLOCK_EXTTS;
+	event.index = 0;
+	event.timestamp = phy2txts(&dp83640->edata);
+
+	ptp_clock_event(dp83640_clock, &event);
+}
+
+static void decode_rxts(struct dp83640_private *dp83640,
+			struct phy_rxts *phy_rxts)
+{
+	struct rxts *rxts;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+
+	if (list_empty(&dp83640->rxpool)) {
+		pr_warning("dp83640: rx timestamp pool is empty\n");
+		goto out;
+	}
+	rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+	list_del_init(&rxts->list);
+	phy2rxts(phy_rxts, rxts);
+	list_add_tail(&rxts->list, &dp83640->rxts);
+out:
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void decode_txts(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+
+	/* We must already have the skb that triggered this. */
+
+	skb = skb_dequeue(&dp83640->tx_queue);
+
+	if (!skb) {
+		pr_warning("dp83640: have timestamp but tx_queue empty\n");
+		return;
+	}
+	ns = phy2txts(phy_txts);
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+	skb_complete_tx_timestamp(skb, &shhwtstamps);
+}
+
+static void decode_status_frame(struct dp83640_private *dp83640,
+				struct sk_buff *skb)
+{
+	struct phy_rxts *phy_rxts;
+	struct phy_txts *phy_txts;
+	u8 *ptr;
+	int len, size;
+	u16 ests, type;
+
+	ptr = skb->data + 2;
+
+	for (len = skb_headlen(skb) - 2; len > sizeof(type); len -= size) {
+
+		type = *(u16 *)ptr;
+		ests = type & 0x0fff;
+		type = type & 0xf000;
+		len -= sizeof(type);
+		ptr += sizeof(type);
+
+		if (PSF_RX == type && len >= sizeof(*phy_rxts)) {
+
+			phy_rxts = (struct phy_rxts *) ptr;
+			decode_rxts(dp83640, phy_rxts);
+			size = sizeof(*phy_rxts);
+
+		} else if (PSF_TX == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_txts(dp83640, phy_txts);
+			size = sizeof(*phy_txts);
+
+		} else if (PSF_EVNT == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_evnt(dp83640, phy_txts, ests);
+			size = sizeof(*phy_txts);
+
+		} else {
+			size = 0;
+			break;
+		}
+		ptr += size;
+	}
+}
+
+static int expired(struct rxts *rxts)
+{
+	return time_after(jiffies, rxts->tmo);
+}
+
+static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
+{
+	u16 *seqid;
+	u8 *msgtype, *data = skb_mac_header(skb);
+
+	/* check sequenceID, messageType, 12 bit hash of offset 20-29 */
+	/* We assume that the IPv4 header has no options. */
+
+	switch (type) {
+	case PTP_CLASS_V1_IPV4:
+		msgtype = data + 42 + 32;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V1_IPV6:
+		msgtype = data + 62 + 32;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_IPV4:
+		msgtype = data + 42 + 0;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V2_IPV6:
+		msgtype = data + 62 + 0;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_L2:
+		msgtype = data + 14 + 0;
+		seqid = (u16 *)(data + 14 + 30);
+		break;
+	case PTP_CLASS_V2_VLAN:
+		msgtype = data + 18 + 0;
+		seqid = (u16 *)(data + 18 + 30);
+		break;
+	default:
+		return 0;
+	}
+
+	return ((*msgtype & 0xf) == rxts->msgtype && *seqid == rxts->seqid);
+}
+
+static int dp83640_probe(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640;
+	int i;
+
+	dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL);
+	if (!dp83640)
+		return -ENOMEM;
+
+	dp83640->phydev = phydev;
+	INIT_WORK(&dp83640->ts_work, do_timestamp_work);
+	mutex_init(&dp83640->extreg_mux);
+
+	INIT_LIST_HEAD(&dp83640->rxts);
+	INIT_LIST_HEAD(&dp83640->rxpool);
+	for (i = 0; i < MAX_RXTS; i++)
+		list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool);
+
+	phydev->priv = dp83640;
+
+	spin_lock_init(&dp83640->rx_lock);
+	skb_queue_head_init(&dp83640->rx_queue);
+	skb_queue_head_init(&dp83640->tx_queue);
+
+	mutex_lock(&clock_lock);
+
+	if (!dp83640_clock) {
+		ptp_dp83640_caps.priv = dp83640;
+		dp83640_clock = ptp_clock_register(&ptp_dp83640_caps);
+		if (IS_ERR(dp83640_clock)) {
+			mutex_unlock(&clock_lock);
+			kfree(dp83640);
+			return PTR_ERR(dp83640_clock);
+		}
+	}
+	mutex_unlock(&clock_lock);
+
+	return 0;
+}
+
+static void dp83640_remove(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	enable_status_frames(phydev, false);
+
+	cancel_work_sync(&dp83640->ts_work);
+
+	mutex_lock(&clock_lock);
+
+	if (ptp_dp83640_caps.priv == dp83640) {
+		ptp_clock_unregister(dp83640_clock);
+		dp83640_clock = NULL;
+		ptp_dp83640_caps.priv = NULL;
+	}
+	mutex_unlock(&clock_lock);
+
+	mutex_destroy(&dp83640->extreg_lock);
+
+	kfree(dp83640);
+}
+
+static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	struct hwtstamp_config cfg;
+	u16 txcfg0, rxcfg0;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.flags) /* reserved for future extensions */
+		return -EINVAL;
+
+	switch (cfg.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		dp83640->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		dp83640->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		dp83640->hwts_rx_en = 0;
+		dp83640->layer = 0;
+		dp83640->version = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 1;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER2;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4|LAYER2;
+		dp83640->version = 2;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+	rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+
+	if (dp83640->layer & LAYER2) {
+		txcfg0 |= TX_L2_EN;
+		rxcfg0 |= RX_L2_EN;
+	}
+	if (dp83640->layer & LAYER4) {
+		txcfg0 |= TX_IPV6_EN | TX_IPV4_EN;
+		rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN;
+	}
+
+	if (dp83640->hwts_tx_en)
+		txcfg0 |= TX_TS_EN;
+
+	if (dp83640->hwts_rx_en)
+		rxcfg0 |= RX_TS_EN;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	if (dp83640->hwts_tx_en || dp83640->hwts_rx_en) {
+		enable_status_frames(phydev, true);
+		ext_write(phydev, PAGE4, PTP_CTL, PTP_ENABLE);
+	}
+
+	ext_write(phydev, PAGE5, PTP_TXCFG0, txcfg0);
+	ext_write(phydev, PAGE5, PTP_RXCFG0, rxcfg0);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static void rx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct list_head *this, *next;
+	struct rxts *rxts;
+	struct skb_shared_hwtstamps *shhwtstamps;
+	struct sk_buff *skb;
+	unsigned int type;
+	unsigned long flags;
+
+	/* Deliver each deferred packet, with or without a time stamp. */
+
+	while ((skb = skb_dequeue(&dp83640->rx_queue)) != NULL) {
+		type = SKB_PTP_TYPE(skb);
+		spin_lock_irqsave(&dp83640->rx_lock, flags);
+		list_for_each_safe(this, next, &dp83640->rxts) {
+			rxts = list_entry(this, struct rxts, list);
+			if (match(skb, type, rxts)) {
+				shhwtstamps = skb_hwtstamps(skb);
+				memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+				shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns);
+				list_del_init(&rxts->list);
+				list_add(&rxts->list, &dp83640->rxpool);
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+		netif_rx(skb);
+	}
+
+	/* Clear out expired time stamps. */
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+	list_for_each_safe(this, next, &dp83640->rxts) {
+		rxts = list_entry(this, struct rxts, list);
+		if (expired(rxts)) {
+			list_del_init(&rxts->list);
+			list_add(&rxts->list, &dp83640->rxpool);
+		}
+	}
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void do_timestamp_work(struct work_struct *work)
+{
+	struct dp83640_private *dp83640 =
+		container_of(work, struct dp83640_private, ts_work);
+
+	rx_reading_work(dp83640);
+	rx_timestamp_work(dp83640);
+	tx_timestamp_work(dp83640);
+}
+
+static bool dp83640_rxtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_rx_en)
+		return false;
+
+	if (is_status_frame(skb, type)) {
+		decode_status_frame(dp83640, skb);
+		/* Let the stack drop this frame. */
+		return false;
+	}
+
+	SKB_PTP_TYPE(skb) = type;
+	skb_queue_tail(&dp83640->rx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+
+	return true;
+}
+
+static void dp83640_txtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_tx_en) {
+		kfree_skb(skb);
+		return;
+	}
+	skb_queue_tail(&dp83640->tx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+}
+
+static struct phy_driver dp83640_driver = {
+	.phy_id		= DP83640_PHY_ID,
+	.phy_id_mask	= 0xfffffff0,
+	.name		= "NatSemi DP83640",
+	.features	= PHY_BASIC_FEATURES,
+	.flags		= 0,
+	.probe		= dp83640_probe,
+	.remove		= dp83640_remove,
+	.config_aneg	= genphy_config_aneg,
+	.read_status	= genphy_read_status,
+	.hwtstamp	= dp83640_hwtstamp,
+	.rxtstamp	= dp83640_rxtstamp,
+	.txtstamp	= dp83640_txtstamp,
+	.driver		= {.owner = THIS_MODULE,}
+};
+
+static int __init dp83640_init(void)
+{
+	return phy_driver_register(&dp83640_driver);
+}
+
+static void __exit dp83640_exit(void)
+{
+	phy_driver_unregister(&dp83640_driver);
+}
+
+MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_LICENSE("GPL");
+
+module_init(dp83640_init);
+module_exit(dp83640_exit);
+
+static struct mdio_device_id dp83640_tbl[] = {
+	{ DP83640_PHY_ID, 0xfffffff0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, dp83640_tbl);
diff --git a/drivers/net/phy/dp83640_reg.h b/drivers/net/phy/dp83640_reg.h
new file mode 100644
index 0000000..9d34bb6
--- /dev/null
+++ b/drivers/net/phy/dp83640_reg.h
@@ -0,0 +1,261 @@
+/* dp83640_reg.h
+ * Generated by regen.tcl on Fri Jul 23 09:45:18 AM CEST 2010
+ */
+#ifndef HAVE_DP83640_REGISTERS
+#define HAVE_DP83640_REGISTERS
+
+#define PAGE4                     0x0004
+#define PTP_CTL                   0x0014 /* PTP Control Register */
+#define PTP_TDR                   0x0015 /* PTP Time Data Register */
+#define PTP_STS                   0x0016 /* PTP Status Register */
+#define PTP_TSTS                  0x0017 /* PTP Trigger Status Register */
+#define PTP_RATEL                 0x0018 /* PTP Rate Low Register */
+#define PTP_RATEH                 0x0019 /* PTP Rate High Register */
+#define PTP_RDCKSUM               0x001a /* PTP Read Checksum */
+#define PTP_WRCKSUM               0x001b /* PTP Write Checksum */
+#define PTP_TXTS                  0x001c /* PTP Transmit Timestamp Register, in four 16-bit reads */
+#define PTP_RXTS                  0x001d /* PTP Receive Timestamp Register, in six? 16-bit reads */
+#define PTP_ESTS                  0x001e /* PTP Event Status Register */
+#define PTP_EDATA                 0x001f /* PTP Event Data Register */
+
+#define PAGE5                     0x0005
+#define PTP_TRIG                  0x0014 /* PTP Trigger Configuration Register */
+#define PTP_EVNT                  0x0015 /* PTP Event Configuration Register */
+#define PTP_TXCFG0                0x0016 /* PTP Transmit Configuration Register 0 */
+#define PTP_TXCFG1                0x0017 /* PTP Transmit Configuration Register 1 */
+#define PSF_CFG0                  0x0018 /* PHY Status Frame Configuration Register 0 */
+#define PTP_RXCFG0                0x0019 /* PTP Receive Configuration Register 0 */
+#define PTP_RXCFG1                0x001a /* PTP Receive Configuration Register 1 */
+#define PTP_RXCFG2                0x001b /* PTP Receive Configuration Register 2 */
+#define PTP_RXCFG3                0x001c /* PTP Receive Configuration Register 3 */
+#define PTP_RXCFG4                0x001d /* PTP Receive Configuration Register 4 */
+#define PTP_TRDL                  0x001e /* PTP Temporary Rate Duration Low Register */
+#define PTP_TRDH                  0x001f /* PTP Temporary Rate Duration High Register */
+
+#define PAGE6                     0x0006
+#define PTP_COC                   0x0014 /* PTP Clock Output Control Register */
+#define PSF_CFG1                  0x0015 /* PHY Status Frame Configuration Register 1 */
+#define PSF_CFG2                  0x0016 /* PHY Status Frame Configuration Register 2 */
+#define PSF_CFG3                  0x0017 /* PHY Status Frame Configuration Register 3 */
+#define PSF_CFG4                  0x0018 /* PHY Status Frame Configuration Register 4 */
+#define PTP_SFDCFG                0x0019 /* PTP SFD Configuration Register */
+#define PTP_INTCTL                0x001a /* PTP Interrupt Control Register */
+#define PTP_CLKSRC                0x001b /* PTP Clock Source Register */
+#define PTP_ETR                   0x001c /* PTP Ethernet Type Register */
+#define PTP_OFF                   0x001d /* PTP Offset Register */
+#define PTP_GPIOMON               0x001e /* PTP GPIO Monitor Register */
+#define PTP_RXHASH                0x001f /* PTP Receive Hash Register */
+
+/* Bit definitions for the PTP_CTL register */
+#define TRIG_SEL_SHIFT            (10) /* PTP Trigger Select */
+#define TRIG_SEL_MASK             (0x7)
+#define TRIG_DIS                  (1<<9) /* Disable PTP Trigger */
+#define TRIG_EN                   (1<<8) /* Enable PTP Trigger */
+#define TRIG_READ                 (1<<7) /* Read PTP Trigger */
+#define TRIG_LOAD                 (1<<6) /* Load PTP Trigger */
+#define PTP_RD_CLK                (1<<5) /* Read PTP Clock */
+#define PTP_LOAD_CLK              (1<<4) /* Load PTP Clock */
+#define PTP_STEP_CLK              (1<<3) /* Step PTP Clock */
+#define PTP_ENABLE                (1<<2) /* Enable PTP Clock */
+#define PTP_DISABLE               (1<<1) /* Disable PTP Clock */
+#define PTP_RESET                 (1<<0) /* Reset PTP Clock */
+
+/* Bit definitions for the PTP_STS register */
+#define TXTS_RDY                  (1<<11) /* Transmit Timestamp Ready */
+#define RXTS_RDY                  (1<<10) /* Receive Timestamp Ready */
+#define TRIG_DONE                 (1<<9) /* PTP Trigger Done */
+#define EVENT_RDY                 (1<<8) /* PTP Event Timestamp Ready */
+#define TXTS_IE                   (1<<3) /* Transmit Timestamp Interrupt Enable */
+#define RXTS_IE                   (1<<2) /* Receive Timestamp Interrupt Enable */
+#define TRIG_IE                   (1<<1) /* Trigger Interrupt Enable */
+#define EVENT_IE                  (1<<0) /* Event Interrupt Enable */
+
+/* Bit definitions for the PTP_TSTS register */
+#define TRIG7_ERROR               (1<<15) /* Trigger 7 Error */
+#define TRIG7_ACTIVE              (1<<14) /* Trigger 7 Active */
+#define TRIG6_ERROR               (1<<13) /* Trigger 6 Error */
+#define TRIG6_ACTIVE              (1<<12) /* Trigger 6 Active */
+#define TRIG5_ERROR               (1<<11) /* Trigger 5 Error */
+#define TRIG5_ACTIVE              (1<<10) /* Trigger 5 Active */
+#define TRIG4_ERROR               (1<<9) /* Trigger 4 Error */
+#define TRIG4_ACTIVE              (1<<8) /* Trigger 4 Active */
+#define TRIG3_ERROR               (1<<7) /* Trigger 3 Error */
+#define TRIG3_ACTIVE              (1<<6) /* Trigger 3 Active */
+#define TRIG2_ERROR               (1<<5) /* Trigger 2 Error */
+#define TRIG2_ACTIVE              (1<<4) /* Trigger 2 Active */
+#define TRIG1_ERROR               (1<<3) /* Trigger 1 Error */
+#define TRIG1_ACTIVE              (1<<2) /* Trigger 1 Active */
+#define TRIG0_ERROR               (1<<1) /* Trigger 0 Error */
+#define TRIG0_ACTIVE              (1<<0) /* Trigger 0 Active */
+
+/* Bit definitions for the PTP_RATEH register */
+#define PTP_RATE_DIR              (1<<15) /* PTP Rate Direction */
+#define PTP_TMP_RATE              (1<<14) /* PTP Temporary Rate */
+#define PTP_RATE_HI_SHIFT         (0) /* PTP Rate High 10-bits */
+#define PTP_RATE_HI_MASK          (0x3ff)
+
+/* Bit definitions for the PTP_ESTS register */
+#define EVNTS_MISSED_SHIFT        (8) /* Indicates number of events missed */
+#define EVNTS_MISSED_MASK         (0x7)
+#define EVNT_TS_LEN_SHIFT         (6) /* Indicates length of the Timestamp field in 16-bit words minus 1 */
+#define EVNT_TS_LEN_MASK          (0x3)
+#define EVNT_RF                   (1<<5) /* Indicates whether the event is a rise or falling event */
+#define EVNT_NUM_SHIFT            (2) /* Indicates Event Timestamp Unit which detected an event */
+#define EVNT_NUM_MASK             (0x7)
+#define MULT_EVNT                 (1<<1) /* Indicates multiple events were detected at the same time */
+#define EVENT_DET                 (1<<0) /* PTP Event Detected */
+
+/* Bit definitions for the PTP_EDATA register */
+#define E7_RISE                   (1<<15) /* Indicates direction of Event 7 */
+#define E7_DET                    (1<<14) /* Indicates Event 7 detected */
+#define E6_RISE                   (1<<13) /* Indicates direction of Event 6 */
+#define E6_DET                    (1<<12) /* Indicates Event 6 detected */
+#define E5_RISE                   (1<<11) /* Indicates direction of Event 5 */
+#define E5_DET                    (1<<10) /* Indicates Event 5 detected */
+#define E4_RISE                   (1<<9) /* Indicates direction of Event 4 */
+#define E4_DET                    (1<<8) /* Indicates Event 4 detected */
+#define E3_RISE                   (1<<7) /* Indicates direction of Event 3 */
+#define E3_DET                    (1<<6) /* Indicates Event 3 detected */
+#define E2_RISE                   (1<<5) /* Indicates direction of Event 2 */
+#define E2_DET                    (1<<4) /* Indicates Event 2 detected */
+#define E1_RISE                   (1<<3) /* Indicates direction of Event 1 */
+#define E1_DET                    (1<<2) /* Indicates Event 1 detected */
+#define E0_RISE                   (1<<1) /* Indicates direction of Event 0 */
+#define E0_DET                    (1<<0) /* Indicates Event 0 detected */
+
+/* Bit definitions for the PTP_TRIG register */
+#define TRIG_PULSE                (1<<15) /* generate a Pulse rather than a single edge */
+#define TRIG_PER                  (1<<14) /* generate a periodic signal */
+#define TRIG_IF_LATE              (1<<13) /* trigger immediately if already past */
+#define TRIG_NOTIFY               (1<<12) /* Trigger Notification Enable */
+#define TRIG_GPIO_SHIFT           (8) /* Trigger GPIO Connection, value 1-12 */
+#define TRIG_GPIO_MASK            (0xf)
+#define TRIG_TOGGLE               (1<<7) /* Trigger Toggle Mode Enable */
+#define TRIG_CSEL_SHIFT           (1) /* Trigger Configuration Select */
+#define TRIG_CSEL_MASK            (0x7)
+#define TRIG_WR                   (1<<0) /* Trigger Configuration Write */
+
+/* Bit definitions for the PTP_EVNT register */
+#define EVNT_RISE                 (1<<14) /* Event Rise Detect Enable */
+#define EVNT_FALL                 (1<<13) /* Event Fall Detect Enable */
+#define EVNT_SINGLE               (1<<12) /* enable single event capture operation */
+#define EVNT_GPIO_SHIFT           (8) /* Event GPIO Connection, value 1-12 */
+#define EVNT_GPIO_MASK            (0xf)
+#define EVNT_SEL_SHIFT            (1) /* Event Select */
+#define EVNT_SEL_MASK             (0x7)
+#define EVNT_WR                   (1<<0) /* Event Configuration Write */
+
+/* Bit definitions for the PTP_TXCFG0 register */
+#define SYNC_1STEP                (1<<15) /* insert timestamp into transmit Sync Messages */
+#define DR_INSERT                 (1<<13) /* Insert Delay_Req Timestamp in Delay_Resp (dangerous) */
+#define NTP_TS_EN                 (1<<12) /* Enable Timestamping of NTP Packets */
+#define IGNORE_2STEP              (1<<11) /* Ignore Two_Step flag for One-Step operation */
+#define CRC_1STEP                 (1<<10) /* Disable checking of CRC for One-Step operation */
+#define CHK_1STEP                 (1<<9) /* Enable UDP Checksum correction for One-Step Operation */
+#define IP1588_EN                 (1<<8) /* Enable IEEE 1588 defined IP address filter */
+#define TX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define TX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define TX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define TX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define TX_PTP_VER_MASK           (0xf)
+#define TX_TS_EN                  (1<<0) /* Transmit Timestamp Enable */
+
+/* Bit definitions for the PTP_TXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG0 register */
+#define MAC_SRC_ADD_SHIFT         (11) /* Status Frame Mac Source Address */
+#define MAC_SRC_ADD_MASK          (0x3)
+#define MIN_PRE_SHIFT             (8) /* Status Frame Minimum Preamble */
+#define MIN_PRE_MASK              (0x7)
+#define PSF_ENDIAN                (1<<7) /* Status Frame Endian Control */
+#define PSF_IPV4                  (1<<6) /* Status Frame IPv4 Enable */
+#define PSF_PCF_RD                (1<<5) /* Control Frame Read PHY Status Frame Enable */
+#define PSF_ERR_EN                (1<<4) /* Error PHY Status Frame Enable */
+#define PSF_TXTS_EN               (1<<3) /* Transmit Timestamp PHY Status Frame Enable */
+#define PSF_RXTS_EN               (1<<2) /* Receive Timestamp PHY Status Frame Enable */
+#define PSF_TRIG_EN               (1<<1) /* Trigger PHY Status Frame Enable */
+#define PSF_EVNT_EN               (1<<0) /* Event PHY Status Frame Enable */
+
+/* Bit definitions for the PTP_RXCFG0 register */
+#define DOMAIN_EN                 (1<<15) /* Domain Match Enable */
+#define ALT_MAST_DIS              (1<<14) /* Alternate Master Timestamp Disable */
+#define USER_IP_SEL               (1<<13) /* Selects portion of IP address accessible thru PTP_RXCFG2 */
+#define USER_IP_EN                (1<<12) /* Enable User-programmed IP address filter */
+#define RX_SLAVE                  (1<<11) /* Receive Slave Only */
+#define IP1588_EN_SHIFT           (8) /* Enable IEEE 1588 defined IP address filters */
+#define IP1588_EN_MASK            (0xf)
+#define RX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define RX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define RX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define RX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define RX_PTP_VER_MASK           (0xf)
+#define RX_TS_EN                  (1<<0) /* Receive Timestamp Enable */
+
+/* Bit definitions for the PTP_RXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG3 register */
+#define TS_MIN_IFG_SHIFT          (12) /* Minimum Inter-frame Gap */
+#define TS_MIN_IFG_MASK           (0xf)
+#define ACC_UDP                   (1<<11) /* Record Timestamp if UDP Checksum Error */
+#define ACC_CRC                   (1<<10) /* Record Timestamp if CRC Error */
+#define TS_APPEND                 (1<<9) /* Append Timestamp for L2 */
+#define TS_INSERT                 (1<<8) /* Enable Timestamp Insertion */
+#define PTP_DOMAIN_SHIFT          (0) /* PTP Message domainNumber field */
+#define PTP_DOMAIN_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG4 register */
+#define IPV4_UDP_MOD              (1<<15) /* Enable IPV4 UDP Modification */
+#define TS_SEC_EN                 (1<<14) /* Enable Timestamp Seconds */
+#define TS_SEC_LEN_SHIFT          (12) /* Inserted Timestamp Seconds Length */
+#define TS_SEC_LEN_MASK           (0x3)
+#define RXTS_NS_OFF_SHIFT         (6) /* Receive Timestamp Nanoseconds offset */
+#define RXTS_NS_OFF_MASK          (0x3f)
+#define RXTS_SEC_OFF_SHIFT        (0) /* Receive Timestamp Seconds offset */
+#define RXTS_SEC_OFF_MASK         (0x3f)
+
+/* Bit definitions for the PTP_COC register */
+#define PTP_CLKOUT_EN             (1<<15) /* PTP Clock Output Enable */
+#define PTP_CLKOUT_SEL            (1<<14) /* PTP Clock Output Source Select */
+#define PTP_CLKOUT_SPEEDSEL       (1<<13) /* PTP Clock Output I/O Speed Select */
+#define PTP_CLKDIV_SHIFT          (0) /* PTP Clock Divide-by Value */
+#define PTP_CLKDIV_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG1 register */
+#define PTPRESERVED_SHIFT         (12) /* PTP v2 reserved field */
+#define PTPRESERVED_MASK          (0xf)
+#define VERSIONPTP_SHIFT          (8) /* PTP v2 versionPTP field */
+#define VERSIONPTP_MASK           (0xf)
+#define TRANSPORT_SPECIFIC_SHIFT  (4) /* PTP v2 Header transportSpecific field */
+#define TRANSPORT_SPECIFIC_MASK   (0xf)
+#define MESSAGETYPE_SHIFT         (0) /* PTP v2 messageType field */
+#define MESSAGETYPE_MASK          (0xf)
+
+/* Bit definitions for the PTP_SFDCFG register */
+#define TX_SFD_GPIO_SHIFT         (4) /* TX SFD GPIO Select, value 1-12 */
+#define TX_SFD_GPIO_MASK          (0xf)
+#define RX_SFD_GPIO_SHIFT         (0) /* RX SFD GPIO Select, value 1-12 */
+#define RX_SFD_GPIO_MASK          (0xf)
+
+/* Bit definitions for the PTP_INTCTL register */
+#define PTP_INT_GPIO_SHIFT        (0) /* PTP Interrupt GPIO Select */
+#define PTP_INT_GPIO_MASK         (0xf)
+
+/* Bit definitions for the PTP_CLKSRC register */
+#define CLK_SRC_SHIFT             (14) /* PTP Clock Source Select */
+#define CLK_SRC_MASK              (0x3)
+#define CLK_SRC_PER_SHIFT         (0) /* PTP Clock Source Period */
+#define CLK_SRC_PER_MASK          (0x7f)
+
+/* Bit definitions for the PTP_OFF register */
+#define PTP_OFFSET_SHIFT          (0) /* PTP Message offset from preceding header */
+#define PTP_OFFSET_MASK           (0xff)
+
+#endif
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 4/5] ptp: Added a clock driver for the IXP46x.
From: Richard Cochran @ 2010-08-16 11:18 UTC (permalink / raw)
  To: netdev
  Cc: linuxppc-dev, devicetree-discuss, linux-kernel, Rodolfo Giometti,
	linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <cover.1281956490.git.richard.cochran@omicron.at>

This patch adds a driver for the hardware time stamping unit found on the
IXP465. The basic clock operations and an external trigger are implemented.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h |   78 ++++++
 drivers/net/arm/ixp4xx_eth.c                  |  191 +++++++++++++
 drivers/ptp/Kconfig                           |   13 +
 drivers/ptp/Makefile                          |    1 +
 drivers/ptp/ptp_ixp46x.c                      |  359 +++++++++++++++++++++++++
 5 files changed, 642 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
 create mode 100644 drivers/ptp/ptp_ixp46x.c

diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
new file mode 100644
index 0000000..729a6b2
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
@@ -0,0 +1,78 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _IXP46X_TS_H_
+#define _IXP46X_TS_H_
+
+#define DEFAULT_ADDEND 0xF0000029
+#define TICKS_NS_SHIFT 4
+
+struct ixp46x_channel_ctl {
+	u32 Ch_Control; /* 0x40 Time Synchronization Channel Control */
+	u32 Ch_Event;   /* 0x44 Time Synchronization Channel Event */
+	u32 TxSnapLo;   /* 0x48 Transmit Snapshot Low Register */
+	u32 TxSnapHi;   /* 0x4C Transmit Snapshot High Register */
+	u32 RxSnapLo;   /* 0x50 Receive Snapshot Low Register */
+	u32 RxSnapHi;   /* 0x54 Receive Snapshot High Register */
+	u32 SrcUUIDLo;  /* 0x58 Source UUID0 Low Register */
+	u32 SrcUUIDHi;  /* 0x5C Sequence Identifier/Source UUID0 High */
+};
+
+struct ixp46x_ts_regs {
+	u32 Control;     /* 0x00 Time Sync Control Register */
+	u32 Event;       /* 0x04 Time Sync Event Register */
+	u32 Addend;      /* 0x08 Time Sync Addend Register */
+	u32 Accum;       /* 0x0C Time Sync Accumulator Register */
+	u32 Test;        /* 0x10 Time Sync Test Register */
+	u32 Unused;      /* 0x14 */
+	u32 RSysTime_Lo; /* 0x18 RawSystemTime_Low Register */
+	u32 RSysTimeHi;  /* 0x1C RawSystemTime_High Register */
+	u32 SysTimeLo;   /* 0x20 SystemTime_Low Register */
+	u32 SysTimeHi;   /* 0x24 SystemTime_High Register */
+	u32 TrgtLo;      /* 0x28 TargetTime_Low Register */
+	u32 TrgtHi;      /* 0x2C TargetTime_High Register */
+	u32 ASMSLo;      /* 0x30 Auxiliary Slave Mode Snapshot Low  */
+	u32 ASMSHi;      /* 0x34 Auxiliary Slave Mode Snapshot High */
+	u32 AMMSLo;      /* 0x38 Auxiliary Master Mode Snapshot Low */
+	u32 AMMSHi;      /* 0x3C Auxiliary Master Mode Snapshot High */
+
+	struct ixp46x_channel_ctl channel[3];
+};
+
+/* 0x00 Time Sync Control Register Bits */
+#define TSCR_AMM (1<<3)
+#define TSCR_ASM (1<<2)
+#define TSCR_TTM (1<<1)
+#define TSCR_RST (1<<0)
+
+/* 0x04 Time Sync Event Register Bits */
+#define TSER_SNM (1<<3)
+#define TSER_SNS (1<<2)
+#define TTIPEND  (1<<1)
+
+/* 0x40 Time Synchronization Channel Control Register Bits */
+#define MASTER_MODE   (1<<0)
+#define TIMESTAMP_ALL (1<<1)
+
+/* 0x44 Time Synchronization Channel Event Register Bits */
+#define TX_SNAPSHOT_LOCKED (1<<0)
+#define RX_SNAPSHOT_LOCKED (1<<1)
+
+#endif
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 4f1cc71..3d36154 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -30,9 +30,12 @@
 #include <linux/etherdevice.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/net_tstamp.h>
 #include <linux/phy.h>
 #include <linux/platform_device.h>
+#include <linux/ptp_classify.h>
 #include <linux/slab.h>
+#include <mach/ixp46x_ts.h>
 #include <mach/npe.h>
 #include <mach/qmgr.h>
 
@@ -67,6 +70,14 @@
 #define RXFREE_QUEUE(port_id)	(NPE_ID(port_id) + 26)
 #define TXDONE_QUEUE		31
 
+#define PTP_SLAVE_MODE		1
+#define PTP_MASTER_MODE		2
+#define PORT2CHANNEL(p)		1
+/*
+ * PHYSICAL_ID(p->id) ?
+ * TODO - Figure out correct mapping.
+ */
+
 /* TX Control Registers */
 #define TX_CNTRL0_TX_EN		0x01
 #define TX_CNTRL0_HALFDUPLEX	0x02
@@ -171,6 +182,8 @@ struct port {
 	int id;			/* logical port ID */
 	int speed, duplex;
 	u8 firmware[4];
+	int hwts_tx_en;
+	int hwts_rx_en;
 };
 
 /* NPE message structure */
@@ -246,6 +259,171 @@ static int ports_open;
 static struct port *npe_port_tab[MAX_NPES];
 static struct dma_pool *dma_pool;
 
+static struct sock_filter ptp_filter[] = {
+	PTP_FILTER
+};
+
+static int match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seq)
+{
+	unsigned int type;
+	u16 *hi, *id;
+	u8 *lo, *data = skb->data;
+
+	type = sk_run_filter(skb, ptp_filter, ARRAY_SIZE(ptp_filter));
+
+	if (PTP_CLASS_V1_IPV4 == type) {
+
+		id = (u16 *)(data + 42 + 30);
+		hi = (u16 *)(data + 42 + 22);
+		lo = data + 42 + 24;
+
+		return (uid_hi == *hi &&
+			0 == memcmp(&uid_lo, lo, sizeof(uid_lo)) &&
+			seq == *id);
+	}
+
+	return 0;
+}
+
+static void do_rx_timestamp(struct port *port, struct sk_buff *skb)
+{
+	struct skb_shared_hwtstamps *shhwtstamps;
+	struct ixp46x_ts_regs *regs;
+	u64 ns;
+	u32 ch, hi, lo, val;
+	u16 uid, seq;
+
+	if (!port->hwts_rx_en)
+		return;
+
+	ch = PORT2CHANNEL(port);
+
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	val = __raw_readl(&regs->channel[ch].Ch_Event);
+
+	if (!(val & RX_SNAPSHOT_LOCKED))
+		return;
+
+	lo = __raw_readl(&regs->channel[ch].SrcUUIDLo);
+	hi = __raw_readl(&regs->channel[ch].SrcUUIDHi);
+
+	uid = hi & 0xffff;
+	seq = (hi >> 16) & 0xffff;
+
+	if (!match(skb, htons(uid), htonl(lo), htons(seq)))
+		goto out;
+
+	lo = __raw_readl(&regs->channel[ch].RxSnapLo);
+	hi = __raw_readl(&regs->channel[ch].RxSnapHi);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	shhwtstamps = skb_hwtstamps(skb);
+	memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+	shhwtstamps->hwtstamp = ns_to_ktime(ns);
+out:
+	__raw_writel(RX_SNAPSHOT_LOCKED, &regs->channel[ch].Ch_Event);
+}
+
+static void do_tx_timestamp(struct port *port, struct sk_buff *skb)
+{
+#ifdef __ARMEB__
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct ixp46x_ts_regs *regs;
+	union skb_shared_tx *shtx;
+	u64 ns;
+	u32 ch, cnt, hi, lo, val;
+
+	shtx = skb_tx(skb);
+	if (unlikely(shtx->hardware && port->hwts_tx_en))
+		shtx->in_progress = 1;
+	else
+		return;
+
+	ch = PORT2CHANNEL(port);
+
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	/*
+	 * This really stinks, but we have to poll for the Tx time stamp.
+	 * Usually, the time stamp is ready after 4 to 6 microseconds.
+	 */
+	for (cnt = 0; cnt < 100; cnt++) {
+		val = __raw_readl(&regs->channel[ch].Ch_Event);
+		if (val & TX_SNAPSHOT_LOCKED)
+			break;
+		udelay(1);
+	}
+	if (!(val & TX_SNAPSHOT_LOCKED)) {
+		shtx->in_progress = 0;
+		return;
+	}
+
+	lo = __raw_readl(&regs->channel[ch].TxSnapLo);
+	hi = __raw_readl(&regs->channel[ch].TxSnapHi);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+	skb_tstamp_tx(skb, &shhwtstamps);
+
+	__raw_writel(TX_SNAPSHOT_LOCKED, &regs->channel[ch].Ch_Event);
+#endif
+}
+
+static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+	struct hwtstamp_config cfg;
+	struct ixp46x_ts_regs *regs;
+	struct port *port = netdev_priv(netdev);
+	int ch;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.flags) /* reserved for future extensions */
+		return -EINVAL;
+
+	ch = PORT2CHANNEL(port);
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	switch (cfg.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		port->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		port->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		port->hwts_rx_en = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+		port->hwts_rx_en = PTP_SLAVE_MODE;
+		__raw_writel(0, &regs->channel[ch].Ch_Control);
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		port->hwts_rx_en = PTP_MASTER_MODE;
+		__raw_writel(MASTER_MODE, &regs->channel[ch].Ch_Control);
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	/* Clear out any old time stamps. */
+	__raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
+		     &regs->channel[ch].Ch_Event);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
 
 static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location,
 			   int write, u16 cmd)
@@ -573,6 +751,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
 
 		debug_pkt(dev, "eth_poll", skb->data, skb->len);
 
+		do_rx_timestamp(port, skb);
 		skb->protocol = eth_type_trans(skb, dev);
 		dev->stats.rx_packets++;
 		dev->stats.rx_bytes += skb->len;
@@ -728,6 +907,10 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
 #if DEBUG_TX
 	printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name);
 #endif
+
+	do_tx_timestamp(port, skb);
+	skb_tx_timestamp(skb);
+
 	return NETDEV_TX_OK;
 }
 
@@ -783,6 +966,9 @@ static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
 	if (!netif_running(dev))
 		return -EINVAL;
 
+	if (cpu_is_ixp46x() && cmd == SIOCSHWTSTAMP)
+		return hwtstamp_ioctl(dev, req, cmd);
+
 	return phy_mii_ioctl(port->phydev, req, cmd);
 }
 
@@ -1171,6 +1357,11 @@ static int __devinit eth_init_one(struct platform_device *pdev)
 	char phy_id[MII_BUS_ID_SIZE + 3];
 	int err;
 
+	if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
+		pr_err("ixp4xx_eth: bad ptp filter\n");
+		return -EINVAL;
+	}
+
 	if (!(dev = alloc_etherdev(sizeof(struct port))))
 		return -ENOMEM;
 
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index aaf957f..cc2d3a1 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -49,4 +49,17 @@ 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_IXP46X
+	tristate "Intel IXP46x as PTP clock"
+	depends on PTP_1588_CLOCK
+	depends on IXP4XX_ETH
+	help
+	  This driver adds support for using the IXP46X 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 ptp_ixp46x.
+
 endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 1651d52..5018f58 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp_clock.o
 obj-$(CONFIG_PTP_1588_CLOCK_LINUX)	+= ptp_linux.o
+obj-$(CONFIG_PTP_1588_CLOCK_IXP46X)	+= ptp_ixp46x.o
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
new file mode 100644
index 0000000..4530256
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,359 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/ptp_clock_kernel.h>
+#include <mach/ixp46x_ts.h>
+
+#define DRIVER		"ptp_ixp46x"
+#define N_EXT_TS	2
+#define MASTER_GPIO	8
+#define MASTER_IRQ	25
+#define SLAVE_GPIO	7
+#define SLAVE_IRQ	24
+
+struct ixp_clock {
+	struct ixp46x_ts_regs *regs;
+	struct ptp_clock *ptp_clock;
+	int exts0_enabled;
+	int exts1_enabled;
+};
+
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static inline u32 ixp_read(volatile unsigned __iomem *addr)
+{
+	u32 val;
+	val = __raw_readl(addr);
+	return val;
+}
+
+static inline void ixp_write(volatile unsigned __iomem *addr, u32 val)
+{
+	__raw_writel(val, addr);
+}
+
+static u64 sys_time_read(struct ixp46x_ts_regs *regs)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	lo = ixp_read(&regs->SysTimeLo);
+	hi = ixp_read(&regs->SysTimeHi);
+
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	return ns;
+}
+
+static void sys_time_write(struct ixp46x_ts_regs *regs, u64 ns)
+{
+	u32 hi, lo;
+
+	ns >>= TICKS_NS_SHIFT;
+	hi = ns >> 32;
+	lo = ns & 0xffffffff;
+
+	ixp_write(&regs->SysTimeLo, lo);
+	ixp_write(&regs->SysTimeHi, hi);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+	struct ptp_clock_event event;
+	u32 ack = 0, lo, hi, val;
+
+	val = ixp_read(&regs->Event);
+
+	if (val & TSER_SNS) {
+		ack |= TSER_SNS;
+		if (ixp_clock->exts0_enabled) {
+			hi = ixp_read(&regs->ASMSHi);
+			lo = ixp_read(&regs->ASMSLo);
+			event.type = PTP_CLOCK_EXTTS;
+			event.index = 0;
+			event.timestamp = ((u64) hi) << 32;
+			event.timestamp |= lo;
+			event.timestamp <<= TICKS_NS_SHIFT;
+			ptp_clock_event(ixp_clock->ptp_clock, &event);
+		}
+	}
+
+	if (val & TSER_SNM) {
+		ack |= TSER_SNM;
+		if (ixp_clock->exts1_enabled) {
+			hi = ixp_read(&regs->AMMSHi);
+			lo = ixp_read(&regs->AMMSLo);
+			event.type = PTP_CLOCK_EXTTS;
+			event.index = 1;
+			event.timestamp = ((u64) hi) << 32;
+			event.timestamp |= lo;
+			event.timestamp <<= TICKS_NS_SHIFT;
+			ptp_clock_event(ixp_clock->ptp_clock, &event);
+		}
+	}
+
+	if (val & TTIPEND)
+		ack |= TTIPEND; /* this bit seems to be always set */
+
+	if (ack) {
+		ixp_write(&regs->Event, ack);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_ixp_adjfreq(void *priv, s32 ppb)
+{
+	u64 adj;
+	u32 diff, addend;
+	int neg_adj = 0;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	if (!ppb)
+		return 0;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	addend = DEFAULT_ADDEND;
+	adj = addend;
+	adj *= ppb;
+	diff = div_u64(adj, 1000000000ULL);
+
+	addend = neg_adj ? addend - diff : addend + diff;
+
+	ixp_write(&regs->Addend, addend);
+
+	return 0;
+}
+
+static int ptp_ixp_adjtime(void *priv, struct timespec *ts)
+{
+	s64 delta, now;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	delta = 1000000000LL * ts->tv_sec;
+	delta += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	now = sys_time_read(regs);
+	now += delta;
+	sys_time_write(regs, now);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_ixp_gettime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	u32 remainder;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	ns = sys_time_read(regs);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->tv_nsec = remainder;
+	return 0;
+}
+
+static int ptp_ixp_settime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	ns = ts->tv_sec * 1000000000ULL;
+	ns += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	sys_time_write(regs, ns);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_ixp_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+	/* We do not offer any ancillary timer features. */
+	return -EOPNOTSUPP;
+}
+
+static int ptp_ixp_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+	/* We do not offer any ancillary timer features. */
+	return -EOPNOTSUPP;
+}
+
+static int ptp_ixp_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct ixp_clock *ixp_clock = priv;
+
+	switch (rq->type) {
+	case PTP_REQUEST_EXTTS:
+		switch (rq->index) {
+		case 0:
+			ixp_clock->exts0_enabled = on ? 1 : 0;
+			break;
+		case 1:
+			ixp_clock->exts1_enabled = on ? 1 : 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return 0;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_ixp_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "IXP46X timer",
+	.max_adj	= 66666655,
+	.n_ext_ts	= N_EXT_TS,
+	.pps		= 0,
+	.priv		= NULL,
+	.adjfreq	= ptp_ixp_adjfreq,
+	.adjtime	= ptp_ixp_adjtime,
+	.gettime	= ptp_ixp_gettime,
+	.settime	= ptp_ixp_settime,
+	.gettimer	= ptp_ixp_gettimer,
+	.settimer	= ptp_ixp_settimer,
+	.enable		= ptp_ixp_enable,
+};
+
+/* module operations */
+
+static struct ixp_clock ixp_clock;
+
+static int setup_interrupt(int gpio)
+{
+	int irq;
+
+	gpio_line_config(gpio, IXP4XX_GPIO_IN);
+
+	irq = gpio_to_irq(gpio);
+
+	if (NO_IRQ == irq)
+		return NO_IRQ;
+
+	if (set_irq_type(irq, IRQF_TRIGGER_FALLING)) {
+		pr_err("cannot set trigger type for irq %d\n", irq);
+		return NO_IRQ;
+	}
+
+	if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
+		pr_err("request_irq failed for irq %d\n", irq);
+		return NO_IRQ;
+	}
+
+	return irq;
+}
+
+static void __exit ptp_ixp_exit(void)
+{
+	free_irq(MASTER_IRQ, &ixp_clock);
+	free_irq(SLAVE_IRQ, &ixp_clock);
+	ptp_clock_unregister(ixp_clock.ptp_clock);
+}
+
+static int __init ptp_ixp_init(void)
+{
+	ixp_clock.regs = 
+		(struct ixp46x_ts_regs __iomem *)IXP4XX_TIMESYNC_BASE_VIRT;
+
+	ptp_ixp_caps.priv = &ixp_clock;
+
+	ixp_clock.ptp_clock = ptp_clock_register(&ptp_ixp_caps);
+
+	if (IS_ERR(ixp_clock.ptp_clock))
+		return PTR_ERR(ixp_clock.ptp_clock);
+
+	ixp_write(&ixp_clock.regs->Addend, DEFAULT_ADDEND);
+	ixp_write(&ixp_clock.regs->TrgtLo, 1);
+	ixp_write(&ixp_clock.regs->TrgtHi, 0);
+	ixp_write(&ixp_clock.regs->Event, TTIPEND);
+
+	if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
+		pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
+		goto no_master;
+	}
+	if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
+		pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
+		goto no_slave;
+	}
+
+	return 0;
+no_slave:
+	free_irq(MASTER_IRQ, &ixp_clock);
+no_master:
+	ptp_clock_unregister(ixp_clock.ptp_clock);
+	return -ENODEV;
+}
+
+module_init(ptp_ixp_init);
+module_exit(ptp_ixp_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 3/5] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Richard Cochran @ 2010-08-16 11:18 UTC (permalink / raw)
  To: netdev
  Cc: linuxppc-dev, devicetree-discuss, linux-kernel, Rodolfo Giometti,
	linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <cover.1281956490.git.richard.cochran@omicron.at>

The eTSEC includes a PTP clock with quite a few features. This patch adds
support for the basic clock adjustment functions, plus two external time
stamps and one alarm.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 Documentation/powerpc/dts-bindings/fsl/tsec.txt |   57 +++
 arch/powerpc/boot/dts/mpc8313erdb.dts           |   14 +
 arch/powerpc/boot/dts/mpc8572ds.dts             |   14 +
 arch/powerpc/boot/dts/p2020ds.dts               |   14 +
 arch/powerpc/boot/dts/p2020rdb.dts              |   14 +
 drivers/net/Makefile                            |    1 +
 drivers/net/gianfar_ptp.c                       |  527 +++++++++++++++++++++++
 drivers/net/gianfar_ptp_reg.h                   |  113 +++++
 drivers/ptp/Kconfig                             |   13 +
 9 files changed, 767 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/gianfar_ptp.c
 create mode 100644 drivers/net/gianfar_ptp_reg.h

diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
index edb7ae1..f6edbb8 100644
--- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
@@ -74,3 +74,60 @@ Example:
 		interrupt-parent = <&mpic>;
 		phy-handle = <&phy0>
 	};
+
+* Gianfar PTP clock nodes
+
+General Properties:
+
+  - compatible   Should be "fsl,etsec-ptp"
+  - reg          Offset and length of the register set for the device
+  - interrupts   There should be at least two interrupts. Some devices
+                 have as many as four PTP related interrupts.
+
+Clock Properties:
+
+  - tclk-period  Timer reference clock period in nanoseconds.
+  - tmr-prsc     Prescaler, divides the output clock.
+  - tmr-add      Frequency compensation value.
+  - cksel        0= external clock, 1= eTSEC system clock, 3= RTC clock input.
+                 Currently the driver only supports choice "1".
+  - tmr-fiper1   Fixed interval period pulse generator.
+  - tmr-fiper2   Fixed interval period pulse generator.
+  - max-adj      Maximum frequency adjustment in parts per billion.
+
+  These properties set the operational parameters for the PTP
+  clock. You must choose these carefully for the clock to work right.
+  Here is how to figure good values:
+
+  TimerOsc     = system clock               MHz
+  tclk_period  = desired clock period       nanoseconds
+  NominalFreq  = 1000 / tclk_period         MHz
+  FreqDivRatio = TimerOsc / NominalFreq     (must be greater that 1.0)
+  tmr_add      = ceil(2^32 / FreqDivRatio)
+  OutputClock  = NominalFreq / tmr_prsc     MHz
+  PulseWidth   = 1 / OutputClock            microseconds
+  FiperFreq1   = desired frequency in Hz
+  FiperDiv1    = 1000000 * OutputClock / FiperFreq1
+  tmr_fiper1   = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
+  max_adj      = 1000000000 * (FreqDivRatio - 1.0) - 1
+
+  The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
+  driver expects that tmr_fiper1 will be correctly set to produce a 1
+  Pulse Per Second (PPS) signal, since this will be offered to the PPS
+  subsystem to synchronize the Linux clock.
+
+Example:
+
+	ptp_clock@24E00 {
+		compatible = "fsl,etsec-ptp";
+		reg = <0x24E00 0xB0>;
+		interrupts = <12 0x8 13 0x8>;
+		interrupt-parent = < &ipic >;
+		tclk-period = <10>;
+		tmr-prsc    = <100>;
+		tmr-add     = <0x999999A4>;
+		cksel       = <0x1>;
+		tmr-fiper1  = <0x3B9AC9F6>;
+		tmr-fiper2  = <0x00018696>;
+		max-adj     = <659999998>;
+	};
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 183f2aa..85a7eaa 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -208,6 +208,20 @@
 			sleep = <&pmc 0x00300000>;
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <12 0x8 13 0x8>;
+			interrupt-parent = < &ipic >;
+			tclk-period = <10>;
+			tmr-prsc    = <100>;
+			tmr-add     = <0x999999A4>;
+			cksel       = <0x1>;
+			tmr-fiper1  = <0x3B9AC9F6>;
+			tmr-fiper2  = <0x00018696>;
+			max-adj     = <659999998>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts
index cafc128..74208cd 100644
--- a/arch/powerpc/boot/dts/mpc8572ds.dts
+++ b/arch/powerpc/boot/dts/mpc8572ds.dts
@@ -324,6 +324,20 @@
 			};
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2 71 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xAAAAAAAB>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x3B9AC9FB>;
+			max-adj = <499999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
index 1101914..39d73bb 100644
--- a/arch/powerpc/boot/dts/p2020ds.dts
+++ b/arch/powerpc/boot/dts/p2020ds.dts
@@ -336,6 +336,20 @@
 			phy_type = "ulpi";
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xCCCCCCCD>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x0001869B>;
+			max-adj = <249999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts
index da4cb0d..5498fb9 100644
--- a/arch/powerpc/boot/dts/p2020rdb.dts
+++ b/arch/powerpc/boot/dts/p2020rdb.dts
@@ -396,6 +396,20 @@
 			phy_type = "ulpi";
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xCCCCCCCD>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x0001869B>;
+			max-adj = <249999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 56e8c27..ea164ba 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ATL2) += atlx/
 obj-$(CONFIG_ATL1E) += atl1e/
 obj-$(CONFIG_ATL1C) += atl1c/
 obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
 obj-$(CONFIG_TEHUTI) += tehuti.o
 obj-$(CONFIG_ENIC) += enic/
 obj-$(CONFIG_JME) += jme.o
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
new file mode 100644
index 0000000..637c8a7
--- /dev/null
+++ b/drivers/net/gianfar_ptp.c
@@ -0,0 +1,527 @@
+/*
+ * PTP 1588 clock using the eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+#include "gianfar_ptp_reg.h"
+#include "gianfar.h"
+
+#define DRIVER		"gianfar_ptp"
+#define N_ALARM		1 /* first alarm is used internally to reset fipers */
+#define N_EXT_TS	2
+#define REG_SIZE	sizeof(struct gianfar_ptp_registers)
+
+struct etsects {
+	struct gianfar_ptp_registers *regs;
+	struct ptp_clock *clock;
+	int irq;
+	u64 alarm_interval; /* for periodic alarm */
+	u64 alarm_value;
+	u32 tclk_period;  /* nanoseconds */
+	u32 tmr_prsc;
+	u32 tmr_add;
+	u32 cksel;
+	u32 tmr_fiper1;
+	u32 tmr_fiper2;
+};
+
+/* Private globals */
+static struct etsects the_clock;
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static u64 tmr_cnt_read(struct etsects *etsects)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	lo = gfar_read(&etsects->regs->tmr_cnt_l);
+	hi = gfar_read(&etsects->regs->tmr_cnt_h);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	return ns;
+}
+
+static void tmr_cnt_write(struct etsects *etsects, u64 ns)
+{
+	u32 hi = ns >> 32;
+	u32 lo = ns & 0xffffffff;
+
+	gfar_write(&etsects->regs->tmr_cnt_l, lo);
+	gfar_write(&etsects->regs->tmr_cnt_h, hi);
+}
+
+static void set_alarm(struct etsects *etsects)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	ns = tmr_cnt_read(etsects) + 1500000000ULL;
+	ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
+	ns -= etsects->tclk_period;
+	hi = ns >> 32;
+	lo = ns & 0xffffffff;
+	gfar_write(&etsects->regs->tmr_alarm1_l, lo);
+	gfar_write(&etsects->regs->tmr_alarm1_h, hi);
+}
+
+static void set_fipers(struct etsects *etsects)
+{
+	u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
+
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl & (~TE));
+	gfar_write(&etsects->regs->tmr_prsc,   etsects->tmr_prsc);
+	gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+	gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+	set_alarm(etsects);
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl|TE);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+	struct etsects *etsects = priv;
+	struct ptp_clock_event event;
+	u64 ns;
+	u32 ack = 0, lo, hi, mask, val;
+
+	val = gfar_read(&etsects->regs->tmr_tevent);
+
+	if (val & ETS1) {
+		ack |= ETS1;
+		hi = gfar_read(&etsects->regs->tmr_etts1_h);
+		lo = gfar_read(&etsects->regs->tmr_etts1_l);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 0;
+		event.timestamp = ((u64) hi) << 32;
+		event.timestamp |= lo;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (val & ETS2) {
+		ack |= ETS2;
+		hi = gfar_read(&etsects->regs->tmr_etts2_h);
+		lo = gfar_read(&etsects->regs->tmr_etts2_l);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 1;
+		event.timestamp = ((u64) hi) << 32;
+		event.timestamp |= lo;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (val & ALM2) {
+		ack |= ALM2;
+		if (etsects->alarm_value) {
+			event.type = PTP_CLOCK_ALARM;
+			event.index = 0;
+			event.timestamp = etsects->alarm_value;
+			ptp_clock_event(etsects->clock, &event);
+		}
+		if (etsects->alarm_interval) {
+			ns = etsects->alarm_value + etsects->alarm_interval;
+			hi = ns >> 32;
+			lo = ns & 0xffffffff;
+			spin_lock(&register_lock);
+			gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+			gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+			spin_unlock(&register_lock);
+			etsects->alarm_value = ns;
+		} else {
+			gfar_write(&etsects->regs->tmr_tevent, ALM2);
+			spin_lock(&register_lock);
+			mask = gfar_read(&etsects->regs->tmr_temask);
+			mask &= ~ALM2EN;
+			gfar_write(&etsects->regs->tmr_temask, mask);
+			spin_unlock(&register_lock);
+			etsects->alarm_value = 0;
+			etsects->alarm_interval = 0;
+		}
+	}
+
+	if (val & PP1) {
+		ack |= PP1;
+		event.type = PTP_CLOCK_PPS;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (ack) {
+		gfar_write(&etsects->regs->tmr_tevent, ack);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_gianfar_adjfreq(void *priv, s32 ppb)
+{
+	u64 adj;
+	u32 diff, tmr_add;
+	int neg_adj = 0;
+	struct etsects *etsects = priv;
+
+	if (!ppb)
+		return 0;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	tmr_add = etsects->tmr_add;
+	adj = tmr_add;
+	adj *= ppb;
+	diff = div_u64(adj, 1000000000ULL);
+
+	tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
+
+	gfar_write(&etsects->regs->tmr_add, tmr_add);
+
+	return 0;
+}
+
+static int ptp_gianfar_adjtime(void *priv, struct timespec *ts)
+{
+	s64 delta, now;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	delta = 1000000000LL * ts->tv_sec;
+	delta += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	now = tmr_cnt_read(etsects);
+	now += delta;
+	tmr_cnt_write(etsects, now);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	set_fipers(etsects);
+
+	return 0;
+}
+
+static int ptp_gianfar_gettime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	u32 remainder;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	ns = tmr_cnt_read(etsects);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->tv_nsec = remainder;
+	return 0;
+}
+
+static int ptp_gianfar_settime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	ns = ts->tv_sec * 1000000000ULL;
+	ns += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	tmr_cnt_write(etsects, ns);
+	set_fipers(etsects);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_gianfar_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+	u64 now, ns;
+	u32 remainder;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	ns = etsects->alarm_interval;
+
+	ts->it_interval.tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->it_interval.tv_nsec = remainder;
+
+	spin_lock_irqsave(&register_lock, flags);
+	now = tmr_cnt_read(etsects);
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	ns = etsects->alarm_value - now;
+
+	ts->it_value.tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->it_value.tv_nsec = remainder;
+
+	return 0;
+}
+
+static int ptp_gianfar_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+	u64 ns;
+	u32 lo, hi, mask;
+	unsigned long flags;
+	struct etsects *etsects = p;
+
+	ns = ts->it_interval.tv_sec * 1000000000ULL;
+	ns += ts->it_interval.tv_nsec;
+
+	etsects->alarm_interval = ns;
+
+	ns = ts->it_value.tv_sec * 1000000000ULL;
+	ns += ts->it_value.tv_nsec;
+
+	if (!ns) {
+		/* Cancel the timer. */
+		etsects->alarm_value = 0;
+		etsects->alarm_interval = 0;
+		return 0;
+	}
+
+	if (!abs) {
+		spin_lock_irqsave(&register_lock, flags);
+		ns += tmr_cnt_read(etsects);
+		spin_unlock_irqrestore(&register_lock, flags);
+	}
+
+	etsects->alarm_value = ns;
+
+	hi = ns >> 32;
+	lo = ns & 0xffffffff;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+	gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+
+	mask = gfar_read(&etsects->regs->tmr_temask);
+	mask |= ALM2EN;
+	gfar_write(&etsects->regs->tmr_temask, mask);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_gianfar_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct etsects *etsects = priv;
+	unsigned long flags;
+	u32 bit, mask;
+
+	switch (rq->type) {
+	case PTP_REQUEST_EXTTS:
+		switch (rq->index) {
+		case 0:
+			bit = ETS1EN;
+			break;
+		case 1:
+			bit = ETS2EN;
+			break;
+		default:
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&register_lock, flags);
+		mask = gfar_read(&etsects->regs->tmr_temask);
+		if (on)
+			mask |= bit;
+		else
+			mask &= ~bit;
+		gfar_write(&etsects->regs->tmr_temask, mask);
+		spin_unlock_irqrestore(&register_lock, flags);
+		return 0;
+
+	case PTP_REQUEST_PPS:
+		spin_lock_irqsave(&register_lock, flags);
+		mask = gfar_read(&etsects->regs->tmr_temask);
+		if (on)
+			mask |= PP1EN;
+		else
+			mask &= ~PP1EN;
+		gfar_write(&etsects->regs->tmr_temask, mask);
+		spin_unlock_irqrestore(&register_lock, flags);
+		return 0;
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_gianfar_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "gianfar clock",
+	.max_adj	= 512000,
+	.n_alarm	= N_ALARM,
+	.n_ext_ts	= N_EXT_TS,
+	.n_per_out	= 0,
+	.pps		= 1,
+	.priv		= &the_clock,
+	.adjfreq	= ptp_gianfar_adjfreq,
+	.adjtime	= ptp_gianfar_adjtime,
+	.gettime	= ptp_gianfar_gettime,
+	.settime	= ptp_gianfar_settime,
+	.gettimer	= ptp_gianfar_gettimer,
+	.settimer	= ptp_gianfar_settimer,
+	.enable		= ptp_gianfar_enable,
+};
+
+/* OF device tree */
+
+static int get_of_u32(struct device_node *node, char *str, u32 *val)
+{
+	int plen;
+	const u32 *prop = of_get_property(node, str, &plen);
+
+	if (!prop || plen != sizeof(*prop))
+		return -1;
+	*val = *prop;
+	return 0;
+}
+
+static int gianfar_ptp_probe(struct of_device *dev,
+			     const struct of_device_id *match)
+{
+	struct device_node *node = dev->dev.of_node;
+	struct etsects *etsects = &the_clock;
+	struct timespec now;
+	u32 tmr_ctrl;
+
+	if (get_of_u32(node, "tclk-period", &etsects->tclk_period) ||
+	    get_of_u32(node, "tmr-prsc", &etsects->tmr_prsc) ||
+	    get_of_u32(node, "tmr-add", &etsects->tmr_add) ||
+	    get_of_u32(node, "cksel", &etsects->cksel) ||
+	    get_of_u32(node, "tmr-fiper1", &etsects->tmr_fiper1) ||
+	    get_of_u32(node, "tmr-fiper2", &etsects->tmr_fiper2) ||
+	    get_of_u32(node, "max-adj", &ptp_gianfar_caps.max_adj)) {
+		pr_err("device tree node missing required elements\n");
+		return -ENODEV;
+	}
+
+	etsects->irq = irq_of_parse_and_map(node, 0);
+
+	if (etsects->irq == NO_IRQ) {
+		pr_err("irq not in device tree\n");
+		return -ENODEV;
+	}
+	if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) {
+		pr_err("request_irq failed\n");
+		return -ENODEV;
+	}
+
+	etsects->regs = of_iomap(node, 0);
+	if (!etsects->regs) {
+		pr_err("of_iomap ptp registers failed\n");
+		return -EINVAL;
+	}
+
+	tmr_ctrl =
+	  (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
+	  (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
+
+	getnstimeofday(&now);
+	ptp_gianfar_settime(etsects, &now);
+
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl);
+	gfar_write(&etsects->regs->tmr_add,    etsects->tmr_add);
+	gfar_write(&etsects->regs->tmr_prsc,   etsects->tmr_prsc);
+	gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+	gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+	set_alarm(etsects);
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl|FS|RTPE|TE);
+
+	etsects->clock = ptp_clock_register(&ptp_gianfar_caps);
+
+	return IS_ERR(etsects->clock) ? PTR_ERR(etsects->clock) : 0;
+}
+
+static int gianfar_ptp_remove(struct of_device *dev)
+{
+	gfar_write(&the_clock.regs->tmr_temask, 0);
+	gfar_write(&the_clock.regs->tmr_ctrl,   0);
+
+	ptp_clock_unregister(the_clock.clock);
+
+	free_irq(the_clock.irq, &the_clock);
+
+	iounmap(the_clock.regs);
+
+	return 0;
+}
+
+static struct of_device_id match_table[] = {
+	{ .compatible = "fsl,etsec-ptp" },
+	{},
+};
+
+static struct of_platform_driver gianfar_ptp_driver = {
+	.driver = {
+		.name		= "gianfar_ptp",
+		.of_match_table	= match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe       = gianfar_ptp_probe,
+	.remove      = gianfar_ptp_remove,
+};
+
+/* module operations */
+
+static int __init ptp_gianfar_init(void)
+{
+	return of_register_platform_driver(&gianfar_ptp_driver);
+}
+
+module_init(ptp_gianfar_init);
+
+static void __exit ptp_gianfar_exit(void)
+{
+	of_unregister_platform_driver(&gianfar_ptp_driver);
+}
+
+module_exit(ptp_gianfar_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the eTSEC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/gianfar_ptp_reg.h b/drivers/net/gianfar_ptp_reg.h
new file mode 100644
index 0000000..95e171f
--- /dev/null
+++ b/drivers/net/gianfar_ptp_reg.h
@@ -0,0 +1,113 @@
+/* gianfar_ptp_reg.h
+ * Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010
+ *
+ * PTP 1588 clock using the gianfar eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GIANFAR_PTP_REG_H_
+#define _GIANFAR_PTP_REG_H_
+
+struct gianfar_ptp_registers {
+	u32 tmr_ctrl;     /* Timer control register */
+	u32 tmr_tevent;   /* Timestamp event register */
+	u32 tmr_temask;   /* Timer event mask register */
+	u32 tmr_pevent;   /* Timestamp event register */
+	u32 tmr_pemask;   /* Timer event mask register */
+	u32 tmr_stat;     /* Timestamp status register */
+	u32 tmr_cnt_h;    /* Timer counter high register */
+	u32 tmr_cnt_l;    /* Timer counter low register */
+	u32 tmr_add;      /* Timer drift compensation addend register */
+	u32 tmr_acc;      /* Timer accumulator register */
+	u32 tmr_prsc;     /* Timer prescale */
+	u8  res1[4];
+	u32 tmroff_h;     /* Timer offset high */
+	u32 tmroff_l;     /* Timer offset low */
+	u8  res2[8];
+	u32 tmr_alarm1_h; /* Timer alarm 1 high register */
+	u32 tmr_alarm1_l; /* Timer alarm 1 high register */
+	u32 tmr_alarm2_h; /* Timer alarm 2 high register */
+	u32 tmr_alarm2_l; /* Timer alarm 2 high register */
+	u8  res3[48];
+	u32 tmr_fiper1;   /* Timer fixed period interval */
+	u32 tmr_fiper2;   /* Timer fixed period interval */
+	u32 tmr_fiper3;   /* Timer fixed period interval */
+	u8  res4[20];
+	u32 tmr_etts1_h;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts1_l;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts2_h;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts2_l;  /* Timestamp of general purpose external trigger */
+};
+
+/* 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 (TSEC_1588_GCLK) output phase. */
+#define CIPH                  (1<<6) /* External oscillator input clock phase. */
+#define TMSR                  (1<<5) /* Timer soft reset. When enabled, it resets all the timer registers and state machines. */
+#define BYP                   (1<<3) /* Bypass drift compensated clock */
+#define TE                    (1<<2) /* 1588 timer enable. If not enabled, all the timer registers and state machines are disabled. */
+#define CKSEL_SHIFT           (0) /* 1588 Timer reference clock source select. */
+#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 equaled alarm time register 2 */
+#define ALM1                  (1<<16) /* Current time equaled alarm time register 1 */
+#define PP1                   (1<<7) /* Indicates that a periodic pulse has been generated based on FIPER1 register */
+#define PP2                   (1<<6) /* Indicates that a periodic pulse has been generated based on FIPER2 register */
+#define PP3                   (1<<5) /* Indicates that a periodic pulse has been generated based on FIPER3 register */
+
+/* Bit definitions for the TMR_TEMASK register */
+#define ETS2EN                (1<<25) /* External trigger 2 timestamp sample event enable */
+#define ETS1EN                (1<<24) /* External trigger 1 timestamp sample event 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) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS2 register */
+#define TXP1                  (1<<8) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS1 register */
+#define RXP                   (1<<0) /* Indicates that a 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)
+
+#endif
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 3aa517a..aaf957f 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -36,4 +36,17 @@ config PTP_1588_CLOCK_LINUX
 	  To compile this driver as a module, choose M here: the module
 	  will be called ptp_linux.
 
+config PTP_1588_CLOCK_GIANFAR
+	tristate "Freescale eTSEC as PTP clock"
+	depends on PTP_1588_CLOCK
+	depends on GIANFAR
+	help
+	  This driver adds support for using the eTSEC 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 gianfar_ptp.
+
 endmenu
-- 
1.7.0.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox