* RE: [PATCH] e500v2 36 bit large physical HID0[EN_MAS7_UPDATE]
From: Aggrwal Poonam-B10812 @ 2010-06-16 7:29 UTC (permalink / raw)
To: Micha Nelissen, linuxppc-dev
In-Reply-To: <4C187616.4000303@neli.hopto.org>
> -----Original Message-----
> From:
linuxppc-dev-bounces+poonam.aggrwal=3Dfreescale.com@lists.ozlabs.org
> [mailto:linuxppc-dev-
> bounces+poonam.aggrwal=3Dfreescale.com@lists.ozlabs.org] On Behalf Of
Micha
> Nelissen
> Sent: Wednesday, June 16, 2010 12:29 PM
> To: linuxppc-dev@lists.ozlabs.org
> Subject: [PATCH] e500v2 36 bit large physical HID0[EN_MAS7_UPDATE]
>=20
> Hi,
>=20
> Attached is a patch to fix large physical address support for the
e500v2
> core. When >4GB addresses are used, the MAS7 register needs to be
valid
> for tlbsx instruction usage.
>=20
> Please review and apply.
[Aggrwal Poonam] This is already being done by u-boot, should linux set
it again?
>=20
> Micha
^ permalink raw reply
* [PATCH] e500v2 36 bit large physical HID0[EN_MAS7_UPDATE]
From: Micha Nelissen @ 2010-06-16 6:58 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 214 bytes --]
Hi,
Attached is a patch to fix large physical address support for the e500v2
core. When >4GB addresses are used, the MAS7 register needs to be valid
for tlbsx instruction usage.
Please review and apply.
Micha
[-- Attachment #2: en-mas7-update.diff --]
[-- Type: text/plain, Size: 1328 bytes --]
diff -u -ru linux-2.6.34/arch/powerpc/include/asm/reg.h linux-2.6.34-fix/arch/powerpc/include/asm/reg.h
--- linux-2.6.34/arch/powerpc/include/asm/reg.h 2010-05-16 23:17:36.000000000 +0200
+++ linux-2.6.34-fix/arch/powerpc/include/asm/reg.h 2010-06-16 08:43:28.000000000 +0200
@@ -272,6 +272,7 @@
#define HID0_DAPUEN (1<<8) /* Debug APU enable */
#define HID0_SGE (1<<7) /* Store Gathering Enable */
#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */
+#define HID0_EN_MAS7_UPDATE (1<<7) /* tlbre/tlbsx update MAS7 - e500v2 */
#define HID0_DCFA (1<<6) /* Data Cache Flush Assist */
#define HID0_LRSTK (1<<4) /* Link register stack - 745x */
#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */
diff -u -ru linux-2.6.34/arch/powerpc/kernel/head_fsl_booke.S linux-2.6.34-fix/arch/powerpc/kernel/head_fsl_booke.S
--- linux-2.6.34/arch/powerpc/kernel/head_fsl_booke.S 2010-05-16 23:17:36.000000000 +0200
+++ linux-2.6.34-fix/arch/powerpc/kernel/head_fsl_booke.S 2010-06-16 08:45:10.000000000 +0200
@@ -328,6 +328,13 @@
oris r2,r2,HID0_DOZE@h
mtspr SPRN_HID0, r2
#endif
+#ifdef CONFIG_PTE_64BIT
+BEGIN_MMU_FTR_SECTION
+ mfspr r2,SPRN_HID0
+ ori r2,r2,HID0_EN_MAS7_UPDATE@l
+ mtspr SPRN_HID0, r2
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS)
+#endif
#if !defined(CONFIG_BDI_SWITCH)
/*
^ permalink raw reply
* Re: Request review of device tree documentation
From: M. Warner Losh @ 2010-06-16 6:52 UTC (permalink / raw)
To: wmb
Cc: nico, microblaze-uclinux, devicetree-discuss, jamie, linuxppc-dev,
mike, ppc6dev, jeremy.kerr, linux-arm-kernel
In-Reply-To: <4C187013.5000400@firmworks.com>
In message: <4C187013.5000400@firmworks.com>
Mitch Bradley <wmb@firmworks.com> writes:
: Mike Rapoport wrote:
: > Mitch Bradley wrote:
: >> Mike Rapoport wrote:
: >>> Mitch Bradley wrote:
: >>>
: >>>> The second topic is the hypothetical use of OFW as a HAL. That will
: >>>> not happen for several reasons. The opposition to the idea is
: >>>> widespread and deeply held, and there are good arguments to support
: >>>> that opposition. Furthermore, the economic conditions necessary for
: >>>> the creation of such a HAL do not exist in the ARM world, nor indeed
: >>>> in the Linux world in general. (The necessary condition is the
: >>>> ability for one company to impose a substantial change by fiat -
: >>>> essentially a monopoly position.)
: >>>>
: >>>> Shall we agree, then, that any further discussion of the HAL issue is
: >>>> "just for fun", and that nobody needs to feel threatened that it would
: >>>> actually happen?
: >>>
: >>> I've recently worked with vendor versions of U-Boot for advanced ARM
: >>> SoCs. There is already *huge* chunk of HAL code in those versions. And
: >>> if there would be possibility to have callbacks into the firmware
: >>> these chunks would only grow, IMHO.
: >>
: >> How can there be HAL code in U-Boot unless there is already the
: >> possibility to have callbacks into the firmware?
: >
: > Currently it aims to abstract hardware from U-Boot and reuse the same
: > HW access code across operating systems and bootloaders. If this code
: > would have callbacks I afraid the things would became worse.
:
: The only way I can understand what you said is if I assume that by
: "callback", you mean the following sequence:
:
: a) U-boot loads and executes the OS, providing to the OS the address
: of some HW access routines that it can use
: b) The OS calls one of those HW access routines
: c) During the execution of that HW access routine, that routine calls
: "back" into the OS, before returning. So a call into the OS is nested
: inside a call into U-boot resident code.
:
: If that is what you are worried about, it is not what we were
: discussing. We were discussing - and many people were against - step
: (b).
:
: Are you saying that step (b) - the OS calling into routines provided
: by U-Boot - is already the status quo?
I don't know about status quo, but it certainly is supported. There's
an option to allow for a secondary boot loader, such as FreeBSD's
/boot/loader, to call back into uboot to read things from
flash/disk/whatever, do network access, etc. Not so much a HAL, but
more of an echo of the functionality provided by PC BIOS functions.
/boot/loader can be viewed as a mini OS that calls back into uboot to
have it do things. Once /boot/loader loads FreeBSD, btw, it and uboot
disappear from the scene, so this isn't exactly a HAL situation...
Warner
: >
: >> It is not HAL if it can't be called.
: >>
: >>>
: >>>
: >>>> The potential for "vendors breaking out of the debugging use case and
: >>>> turning it into a HAL" is miniscule, because
: >>>>
: >>>> a) The callback is disabled by default
: >>>> b) The technical challenges of the callback interface limit its
: >>>> applicability to specific "wizard user" scenarios
: >>>> c) OFW is unlikely to achieve sufficient market penetration for the
: >>>> HAL thing to be worth doing
: >>>>
: >>>>
: >>>> _______________________________________________
: >>>> linux-arm-kernel mailing list
: >>>> linux-arm-kernel@lists.infradead.org
: >>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
: >>>
: >>>
: >
: >
: _______________________________________________
: devicetree-discuss mailing list
: devicetree-discuss@lists.ozlabs.org
: https://lists.ozlabs.org/listinfo/devicetree-discuss
:
:
^ permalink raw reply
* Re: Request review of device tree documentation
From: Mike Rapoport @ 2010-06-16 6:09 UTC (permalink / raw)
To: Mitch Bradley
Cc: Nicolas Pitre, microblaze-uclinux, devicetree-discuss,
Jamie Lokier, linuxppc-dev, Mike Rapoport, Dan Malek, Jeremy Kerr,
linux-arm-kernel, David Gibson
In-Reply-To: <4C165FD1.6080505@firmworks.com>
Mitch Bradley wrote:
> The second topic is the hypothetical use of OFW as a HAL. That will not
> happen for several reasons. The opposition to the idea is widespread
> and deeply held, and there are good arguments to support that
> opposition. Furthermore, the economic conditions necessary for the
> creation of such a HAL do not exist in the ARM world, nor indeed in the
> Linux world in general. (The necessary condition is the ability for one
> company to impose a substantial change by fiat - essentially a monopoly
> position.)
>
> Shall we agree, then, that any further discussion of the HAL issue is
> "just for fun", and that nobody needs to feel threatened that it would
> actually happen?
I've recently worked with vendor versions of U-Boot for advanced ARM
SoCs. There is already *huge* chunk of HAL code in those versions. And
if there would be possibility to have callbacks into the firmware these
chunks would only grow, IMHO.
> The potential for "vendors breaking out of the debugging use case and
> turning it into a HAL" is miniscule, because
>
> a) The callback is disabled by default
> b) The technical challenges of the callback interface limit its
> applicability to specific "wizard user" scenarios
> c) OFW is unlikely to achieve sufficient market penetration for the HAL
> thing to be worth doing
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH 11/12] ptp: Added a clock driver for the IXP46x.
From: Richard Cochran @ 2010-06-16 6:54 UTC (permalink / raw)
To: Grant Likely
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <AANLkTinPDaWNvUG8q8RdvRUga-qrPlSV2H5TkzVVMaFn@mail.gmail.com>
On Tue, Jun 15, 2010 at 12:41:56PM -0600, Grant Likely wrote:
> Nitpick. We use all lower case names for structures in Linux.
Yes, I know, but in this case an exception makes sense.
I prefer to use the exact same register mnemonics as in the hardware
documentation, whenever possible. That way, anyone later working on
the driver with hardware manual in hand (and they should be doing that
way) will immediately see the connection.
> You want to get stuff as fast as possible, but there is a udelay()
> that just chews up CPU time. Would cpu_relax() be sufficient with a
> time-based exit condition in the loop?
I am not sure. What does cpu_relax() do exactly, and when is it safe
to call?
Thanks,
Richard
^ permalink raw reply
* Re: Request review of device tree documentation
From: Mike Rapoport @ 2010-06-16 6:47 UTC (permalink / raw)
To: Mitch Bradley
Cc: Nicolas Pitre, microblaze-uclinux, devicetree-discuss,
Jamie Lokier, linuxppc-dev, Mike Rapoport, Dan Malek, Jeremy Kerr,
linux-arm-kernel, David Gibson
In-Reply-To: <4C187013.5000400@firmworks.com>
Mitch Bradley wrote:
> Mike Rapoport wrote:
>> Mitch Bradley wrote:
>>> Mike Rapoport wrote:
>>>> Mitch Bradley wrote:
>>>>
>>>>> The second topic is the hypothetical use of OFW as a HAL. That will
>>>>> not happen for several reasons. The opposition to the idea is
>>>>> widespread and deeply held, and there are good arguments to support
>>>>> that opposition. Furthermore, the economic conditions necessary
>>>>> for the creation of such a HAL do not exist in the ARM world, nor
>>>>> indeed in the Linux world in general. (The necessary condition is
>>>>> the ability for one company to impose a substantial change by fiat
>>>>> - essentially a monopoly position.)
>>>>>
>>>>> Shall we agree, then, that any further discussion of the HAL issue
>>>>> is "just for fun", and that nobody needs to feel threatened that it
>>>>> would actually happen?
>>>>
>>>> I've recently worked with vendor versions of U-Boot for advanced ARM
>>>> SoCs. There is already *huge* chunk of HAL code in those versions.
>>>> And if there would be possibility to have callbacks into the
>>>> firmware these chunks would only grow, IMHO.
>>>
>>> How can there be HAL code in U-Boot unless there is already the
>>> possibility to have callbacks into the firmware?
>>
>> Currently it aims to abstract hardware from U-Boot and reuse the same
>> HW access code across operating systems and bootloaders. If this code
>> would have callbacks I afraid the things would became worse.
>
> The only way I can understand what you said is if I assume that by
> "callback", you mean the following sequence:
>
> a) U-boot loads and executes the OS, providing to the OS the address of
> some HW access routines that it can use
> b) The OS calls one of those HW access routines
> c) During the execution of that HW access routine, that routine calls
> "back" into the OS, before returning. So a call into the OS is nested
> inside a call into U-boot resident code.
>
> If that is what you are worried about, it is not what we were
> discussing. We were discussing - and many people were against - step (b).
>
> Are you saying that step (b) - the OS calling into routines provided by
> U-Boot - is already the status quo?
I'm also objecting the step (b) and, fortunately, it's not yet the
status quo.
Current U-Boot/kernel implementations I've encountered still do not have
OS calls to resident HW access routines. But if such calls would be
allowed, my impression is that SoC vendors would make extensive use of them.
>>
>>> It is not HAL if it can't be called.
>>>
>>>>
>>>>
>>>>> The potential for "vendors breaking out of the debugging use case
>>>>> and turning it into a HAL" is miniscule, because
>>>>>
>>>>> a) The callback is disabled by default
>>>>> b) The technical challenges of the callback interface limit its
>>>>> applicability to specific "wizard user" scenarios
>>>>> c) OFW is unlikely to achieve sufficient market penetration for the
>>>>> HAL thing to be worth doing
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> linux-arm-kernel@lists.infradead.org
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>
>>>>
>>
>>
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH 10/12] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Richard Cochran @ 2010-06-16 6:45 UTC (permalink / raw)
To: Grant Likely
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <AANLkTikFc15j-Qhw9M2CnKLKq58Wi1xD7E2dtVNsVgeA@mail.gmail.com>
On Tue, Jun 15, 2010 at 11:20:41AM -0600, Grant Likely wrote:
>
> Is this header file used by anything other than gianfar_ptp.c? If
> not, then roll the two files together.
I anticipate that it might be necessary to share the header's contents
with gianfar.c one day.
> Use dash ('-') not underscore ('_') in property names.
Okay, can do.
> If you encode this value as a string, then it will be friendly for humans too.
Okay.
> I could use more explication here. Is this a divider value?
> Computers are good at making calculations, and the driver can obtain
> the clock frequency supplied to the device. It may be more useful to
> specify here the desired frequency rather than the divider. Certainly
> more human-friendly too.
It is not that simple. The basic algorithm is described in the text,
and anyone wanting to use the eTSEC for PTP will have to consider the
issue themselves, since it is a design issue with a few tradeoffs
related to the board layout. It is really too thorny to do in the
driver automatically.
I can post a tcltk calculator script for finding appropriate values,
in anyone would like to see it.
> > +static struct etsects the_clock;
>
> Will there ever be multiple instances of this device?
No, never. If you consider how PTP works, there can only be one clock
per system.
> Consider of_iomap(), it will simplify the code a bit.
> Move ptp_gianfar_exit() definition here so it is immediately before
> the module_exit() line.
Okay.
Thanks for the review,
Richard
^ permalink raw reply
* Re: Request review of device tree documentation
From: Mitch Bradley @ 2010-06-16 6:32 UTC (permalink / raw)
To: Mike Rapoport
Cc: Nicolas Pitre, microblaze-uclinux, devicetree-discuss,
Jamie Lokier, linuxppc-dev, Dan Malek, Jeremy Kerr,
linux-arm-kernel, David Gibson
In-Reply-To: <4C186C72.2020506@compulab.co.il>
Mike Rapoport wrote:
> Mitch Bradley wrote:
>> Mike Rapoport wrote:
>>> Mitch Bradley wrote:
>>>
>>>> The second topic is the hypothetical use of OFW as a HAL. That will
>>>> not happen for several reasons. The opposition to the idea is
>>>> widespread and deeply held, and there are good arguments to support
>>>> that opposition. Furthermore, the economic conditions necessary
>>>> for the creation of such a HAL do not exist in the ARM world, nor
>>>> indeed in the Linux world in general. (The necessary condition is
>>>> the ability for one company to impose a substantial change by fiat
>>>> - essentially a monopoly position.)
>>>>
>>>> Shall we agree, then, that any further discussion of the HAL issue
>>>> is "just for fun", and that nobody needs to feel threatened that it
>>>> would actually happen?
>>>
>>> I've recently worked with vendor versions of U-Boot for advanced ARM
>>> SoCs. There is already *huge* chunk of HAL code in those versions.
>>> And if there would be possibility to have callbacks into the
>>> firmware these chunks would only grow, IMHO.
>>
>> How can there be HAL code in U-Boot unless there is already the
>> possibility to have callbacks into the firmware?
>
> Currently it aims to abstract hardware from U-Boot and reuse the same
> HW access code across operating systems and bootloaders. If this code
> would have callbacks I afraid the things would became worse.
The only way I can understand what you said is if I assume that by
"callback", you mean the following sequence:
a) U-boot loads and executes the OS, providing to the OS the address of
some HW access routines that it can use
b) The OS calls one of those HW access routines
c) During the execution of that HW access routine, that routine calls
"back" into the OS, before returning. So a call into the OS is nested
inside a call into U-boot resident code.
If that is what you are worried about, it is not what we were
discussing. We were discussing - and many people were against - step (b).
Are you saying that step (b) - the OS calling into routines provided by
U-Boot - is already the status quo?
>
>> It is not HAL if it can't be called.
>>
>>>
>>>
>>>> The potential for "vendors breaking out of the debugging use case
>>>> and turning it into a HAL" is miniscule, because
>>>>
>>>> a) The callback is disabled by default
>>>> b) The technical challenges of the callback interface limit its
>>>> applicability to specific "wizard user" scenarios
>>>> c) OFW is unlikely to achieve sufficient market penetration for the
>>>> HAL thing to be worth doing
>>>>
>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> linux-arm-kernel@lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>
>>>
>
>
^ permalink raw reply
* Re: [PATCH 04/12] phylib: add a way to make PHY time stamps possible.
From: Richard Cochran @ 2010-06-16 6:29 UTC (permalink / raw)
To: netdev; +Cc: devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <27c0ad283f025c2bb71e7ceb71be07f969939429.1276615626.git.richard.cochran@omicron.at>
On Tue, Jun 15, 2010 at 06:08:20PM +0200, Richard Cochran wrote:
> +static inline void skb_tx_timetamp(struct phy_device *phy, struct sk_buff *skb)
> +{
> + union skb_shared_tx *shtx = skb_tx(skb);
> +
> + if (shtx->hardware && phy && phy->drv->txtstamp)
> + phy->drv->txtstamp(phy, skb);
> +
> + if (shtx->software && !shtx->in_progress)
> + skb_tstamp_tx(skb, NULL);
> +}
I forgot to mention this patch also provides a way to fix the broken
software timestamp fallback mode of the SO_TIMESTAMPING API.
We would have to add this inline call to every MAC driver in an
appropriate spot within the hard_xmit function. It is not too pretty,
but providing this as a compile time option will promote
standardization of the SO_TIMESTAMPING API for applications.
Richard
^ permalink raw reply
* Re: [PATCH 05/12] phylib: Allow reading and writing a mii bus from atomic context.
From: Richard Cochran @ 2010-06-16 6:20 UTC (permalink / raw)
To: Grant Likely
Cc: netdev, devicetree-discuss, Thomas Gleixner, linuxppc-dev,
linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <AANLkTin8RI4UKzA58k_qER_urdwnD037u5GZrVQS8IoS@mail.gmail.com>
> That's right, and I fully agree with that change. To me, going back
> to allowing spin locks is a regression because it adds a new source of
> scheduling latency.
I think that the change was not about reducing scheduling
latency. Rather, the idea was simply to allow mdio bus drivers that
sleep. Here is the change log message:
commit 35b5f6b1a82b5c586e0b24c711dc6ba944e88ef1
PHYLIB: Locking fixes for PHY I/O potentially sleeping
PHY read/write functions can potentially sleep (e.g., a PHY accessed
via I2C). The following changes were made to account for this:
* Change spin locks to mutex locks
* Add a BUG_ON() to phy_read() phy_write() to warn against
calling them from an interrupt context.
* Use work queue for PHY state machine handling since
it can potentially sleep
* Change phydev lock from spinlock to mutex
The fundamental issue is this: Fro the SO_TIMESTAMPING API, receive
timestamps must appear in a control message along with the packet
data. Only the MAC driver (or the PHY driver) knows how to get the
timestamp. The stack calls the MAC driver via its napi poll
function. During the call, the driver must provide the skb with Rx
timestamp.
The only reasonable way to do this is to have the driver fetch the
timestamp durng the napi poll function. For MAC drivers with fast
register access, the performance penalty is small. For PHY drivers
with must go via the MDIO bus, the performance penalty is obviously
larger, and the user must be willing to live with it.
You might suggest the alternate that the driver would defer the
netif_receive_skb() callback until a work queue completes, providing
the Rx timestamp. The driver would then call netif_receive_skb() at
some later time.
However, there are a number of problems with this idea:
1. It is really icky for the drivers to be creating new skb queues for
this purpose. MAC drivers would have to maintain such queues on
behalf of the PHY drivers, but only when the PHYs support
timestamping. Yuck.
2. There is a (soft) real time constraint on the delivery of the PTP
packets to the user space application. Basicly, delays and jitter
in the time to receive the packet negatively affect the clock servo
loop.
3. It cannot work for many kinds of PTP timestamping hardware. Some of
hardware only timestamps PTP packets. That means that not every
received packet will have a timestamp. Such hardware provides some
key data from the packet (like PTP UUID and sequence number) with
the timestamp. Software must match this information to a particular
packet. In order to defer a skb, the driver must first obtain the
timestamp information. This is a catch-22.
Having said all that, I am still open to suggestions...
Richard
^ permalink raw reply
* Re: Request review of device tree documentation
From: Mike Rapoport @ 2010-06-16 6:17 UTC (permalink / raw)
To: Mitch Bradley
Cc: Nicolas Pitre, microblaze-uclinux, devicetree-discuss,
Jamie Lokier, linuxppc-dev, Dan Malek, Jeremy Kerr,
linux-arm-kernel, David Gibson
In-Reply-To: <4C186B7B.1060308@firmworks.com>
Mitch Bradley wrote:
> Mike Rapoport wrote:
>> Mitch Bradley wrote:
>>
>>> The second topic is the hypothetical use of OFW as a HAL. That will
>>> not happen for several reasons. The opposition to the idea is
>>> widespread and deeply held, and there are good arguments to support
>>> that opposition. Furthermore, the economic conditions necessary for
>>> the creation of such a HAL do not exist in the ARM world, nor indeed
>>> in the Linux world in general. (The necessary condition is the
>>> ability for one company to impose a substantial change by fiat -
>>> essentially a monopoly position.)
>>>
>>> Shall we agree, then, that any further discussion of the HAL issue is
>>> "just for fun", and that nobody needs to feel threatened that it
>>> would actually happen?
>>
>> I've recently worked with vendor versions of U-Boot for advanced ARM
>> SoCs. There is already *huge* chunk of HAL code in those versions. And
>> if there would be possibility to have callbacks into the firmware
>> these chunks would only grow, IMHO.
>
> How can there be HAL code in U-Boot unless there is already the
> possibility to have callbacks into the firmware?
Currently it aims to abstract hardware from U-Boot and reuse the same HW
access code across operating systems and bootloaders. If this code would
have callbacks I afraid the things would became worse.
> It is not HAL if it can't be called.
>
>>
>>
>>> The potential for "vendors breaking out of the debugging use case and
>>> turning it into a HAL" is miniscule, because
>>>
>>> a) The callback is disabled by default
>>> b) The technical challenges of the callback interface limit its
>>> applicability to specific "wizard user" scenarios
>>> c) OFW is unlikely to achieve sufficient market penetration for the
>>> HAL thing to be worth doing
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>>
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: Request review of device tree documentation
From: Mitch Bradley @ 2010-06-16 6:13 UTC (permalink / raw)
To: Mike Rapoport
Cc: Nicolas Pitre, microblaze-uclinux, devicetree-discuss,
Jamie Lokier, linuxppc-dev, Dan Malek, Jeremy Kerr,
linux-arm-kernel, David Gibson
In-Reply-To: <4C186AA8.4040709@compulab.co.il>
Mike Rapoport wrote:
> Mitch Bradley wrote:
>
>> The second topic is the hypothetical use of OFW as a HAL. That will
>> not happen for several reasons. The opposition to the idea is
>> widespread and deeply held, and there are good arguments to support
>> that opposition. Furthermore, the economic conditions necessary for
>> the creation of such a HAL do not exist in the ARM world, nor indeed
>> in the Linux world in general. (The necessary condition is the
>> ability for one company to impose a substantial change by fiat -
>> essentially a monopoly position.)
>>
>> Shall we agree, then, that any further discussion of the HAL issue is
>> "just for fun", and that nobody needs to feel threatened that it
>> would actually happen?
>
> I've recently worked with vendor versions of U-Boot for advanced ARM
> SoCs. There is already *huge* chunk of HAL code in those versions. And
> if there would be possibility to have callbacks into the firmware
> these chunks would only grow, IMHO.
How can there be HAL code in U-Boot unless there is already the
possibility to have callbacks into the firmware?
It is not HAL if it can't be called.
>
>
>> The potential for "vendors breaking out of the debugging use case and
>> turning it into a HAL" is miniscule, because
>>
>> a) The callback is disabled by default
>> b) The technical challenges of the callback interface limit its
>> applicability to specific "wizard user" scenarios
>> c) OFW is unlikely to achieve sufficient market penetration for the
>> HAL thing to be worth doing
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>
^ permalink raw reply
* Re: [PATCH 04/12] phylib: add a way to make PHY time stamps possible.
From: Richard Cochran @ 2010-06-16 5:40 UTC (permalink / raw)
To: Grant Likely
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <AANLkTimhAvu0_WuMCwnCrw9Rbcy6dKxvTlzepJm7ZDdR@mail.gmail.com>
On Tue, Jun 15, 2010 at 10:33:51AM -0600, Grant Likely wrote:
> > +config NETWORK_PHY_TIMESTAMPING
> Some overhead? At a brief glance of the series it looks like it could
> add a lot of overhead, but I'm not fully clear on what the full
> process is. Can you describe how the hardware timestamping works? I
> could use an overview of what the kernel has to do.
First of all, I want to emphasize that this network stack option is
purely voluntary. Only those people who know that they have a PTP
capable PHY and really want the timestamps will (or should) enable
this option. When it is not enabled, it has no effect at all.
Hardware timestamping is described in
Documentation/networking/timestamping.txt
Documentation/networking/timestamping/timestamping.c
The PTP subsystem is described in
Documentation/ptp/ptp.txt
There really is more to say about the issue than appears in those
documents, but they are a good starting place for discussion.
BTW I am submitting a conference paper on the design on the PTP
subsystem. If you would like to have it, just ask me off-list.
Richard
^ permalink raw reply
* Re: [PATCH] Restore kexec uImage-ppc to working state
From: Simon Horman @ 2010-06-16 2:38 UTC (permalink / raw)
To: Matthew McClintock; +Cc: linuxppc-dev, kexec
In-Reply-To: <1276544322-30412-1-git-send-email-msm@freescale.com>
CCed linuxppc-dev to fish for an ack.
On Mon, Jun 14, 2010 at 02:38:42PM -0500, Matthew McClintock wrote:
> Booting with uImage-ppc was broken by previous work, this
> patch should restore it to working order
>
> Signed-off-by: Matthew McClintock <msm@freescale.com>
> ---
> kexec/arch/ppc/kexec-ppc.c | 68 ++++++++++++++++++++++-------------
> kexec/arch/ppc/kexec-uImage-ppc.c | 5 +--
> purgatory/arch/ppc/purgatory-ppc.c | 5 +++
> 3 files changed, 49 insertions(+), 29 deletions(-)
>
> diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
> index 55cadd6..c073f56 100644
> --- a/kexec/arch/ppc/kexec-ppc.c
> +++ b/kexec/arch/ppc/kexec-ppc.c
> @@ -261,11 +261,28 @@ static int get_base_ranges(void)
> break;
> }
> }
> - base_memory_range[local_memory_ranges].start =
> - ((uint32_t *)buf)[0];
> - base_memory_range[local_memory_ranges].end =
> - base_memory_range[local_memory_ranges].start +
> - ((uint32_t *)buf)[1];
> +
> + if (n == 8)
> + {
> + base_memory_range[local_memory_ranges].start =
> + ((uint32_t *)buf)[0];
> + base_memory_range[local_memory_ranges].end =
> + base_memory_range[local_memory_ranges].start +
> + ((uint32_t *)buf)[1];
> + }
> + else if (n == 16)
> + {
> + base_memory_range[local_memory_ranges].start =
> + ((uint64_t *)buf)[0];
> + base_memory_range[local_memory_ranges].end =
> + base_memory_range[local_memory_ranges].start +
> + ((uint64_t *)buf)[1];
> + }
> + else
> + {
> + fprintf(stderr, "Mem node has invalid size: %d\n", n);
> + return -1;
> + }
> base_memory_range[local_memory_ranges].type = RANGE_RAM;
> local_memory_ranges++;
> dbgprintf("%016llx-%016llx : %x\n",
> @@ -327,27 +344,28 @@ static int get_devtree_details(unsigned long kexec_flags)
> }
>
> if (strncmp(dentry->d_name, "chosen", 6) == 0) {
> - strcat(fname, "/linux,kernel-end");
> - file = fopen(fname, "r");
> - if (!file) {
> - perror(fname);
> - goto error_opencdir;
> - }
> - if (fread(&tmp_long, sizeof(unsigned long), 1, file)
> - != 1) {
> - perror(fname);
> - goto error_openfile;
> - }
> - kernel_end = tmp_long;
> - fclose(file);
> -
> - /* Add kernel memory to exclude_range */
> - exclude_range[i].start = 0x0UL;
> - exclude_range[i].end = kernel_end;
> - i++;
> - if (i >= max_memory_ranges)
> - realloc_memory_ranges();
> + /* only reserve kernel region if we are doing a crash kernel */
> if (kexec_flags & KEXEC_ON_CRASH) {
> + strcat(fname, "/linux,kernel-end");
> + file = fopen(fname, "r");
> + if (!file) {
> + perror(fname);
> + goto error_opencdir;
> + }
> + if (fread(&tmp_long, sizeof(unsigned long), 1, file)
> + != 1) {
> + perror(fname);
> + goto error_openfile;
> + }
> + kernel_end = tmp_long;
> + fclose(file);
> +
> + /* Add kernel memory to exclude_range */
> + exclude_range[i].start = 0x0UL;
> + exclude_range[i].end = kernel_end;
> + i++;
> + if (i >= max_memory_ranges)
> + realloc_memory_ranges();
> memset(fname, 0, sizeof(fname));
> strcpy(fname, device_tree);
> strcat(fname, dentry->d_name);
> diff --git a/kexec/arch/ppc/kexec-uImage-ppc.c b/kexec/arch/ppc/kexec-uImage-ppc.c
> index 45cde2f..4a8d28d 100644
> --- a/kexec/arch/ppc/kexec-uImage-ppc.c
> +++ b/kexec/arch/ppc/kexec-uImage-ppc.c
> @@ -133,13 +133,10 @@ static int ppc_load_bare_bits(int argc, char **argv, const char *buf,
> addr = dtb_addr;
> elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr));
>
> - addr = rmo_top;
> - elf_rel_set_symbol(&info->rhdr, "mem_size", &addr, sizeof(addr));
> -
> #define PUL_STACK_SIZE (16 * 1024)
> addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, -1, 1);
> addr += PUL_STACK_SIZE;
> - elf_rel_set_symbol(&info->rhdr, "pul_stack", &addr, sizeof(addr));
> + elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr));
> /* No allocation past here in order not to overwrite the stack */
> #undef PUL_STACK_SIZE
>
> diff --git a/purgatory/arch/ppc/purgatory-ppc.c b/purgatory/arch/ppc/purgatory-ppc.c
> index 3d7d484..349e750 100644
> --- a/purgatory/arch/ppc/purgatory-ppc.c
> +++ b/purgatory/arch/ppc/purgatory-ppc.c
> @@ -39,3 +39,8 @@ void post_verification_setup_arch(void)
> if (panic_kernel)
> crashdump_backup_memory();
> }
> +
> +void crashdump_backup_memory(void)
> +{
> + return;
> +}
> --
> 1.6.0.6
>
>
> _______________________________________________
> kexec mailing list
> kexec@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply
* [PATCH RFC] ppc: fix default_machine_crash_shutdown #ifdef botch
From: Paul E. McKenney @ 2010-06-16 0:48 UTC (permalink / raw)
To: linuxppc-dev
crash_kexec_wait_realmode() is defined only if CONFIG_PPC_STD_MMU_64
and CONFIG_SMP, but is called if CONFIG_PPC_STD_MMU_64 even if !CONFIG_SMP.
Fix the conditional compilation around the invocation.
Untested, probably does not compile.
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
---
diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c
index b46f2e0..29df48f 100644
--- a/arch/powerpc/kernel/crash.c
+++ b/arch/powerpc/kernel/crash.c
@@ -447,7 +447,7 @@ void default_machine_crash_shutdown(struct pt_regs *regs)
crash_kexec_prepare_cpus(crashing_cpu);
cpu_set(crashing_cpu, cpus_in_crash);
crash_kexec_stop_spus();
-#ifdef CONFIG_PPC_STD_MMU_64
+#if defined(CONFIG_PPC_STD_MMU_64) && defined(CONFIG_SMP)
crash_kexec_wait_realmode(crashing_cpu);
#endif
if (ppc_md.kexec_cpu_down)
^ permalink raw reply related
* Re: [PATCH 5/5] of/address: restrict 'no-ranges' kludge to powerpc
From: Benjamin Herrenschmidt @ 2010-06-16 0:33 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: Stephen Rothwell, devicetree-discuss, linuxppc-dev
In-Reply-To: <1C7A9067-DDE6-47D3-AC78-FDC081354519@kernel.crashing.org>
On Tue, 2010-06-15 at 18:23 +0200, Segher Boessenkool wrote:
> >> Certain Apple machines don't use the ranges property correctly,
> >> but the
> >> workaround should not be applied on other architectures. This patch
> >> disables the workaround for non-powerpc architectures.
> >
> > I'm half tempted to add it to the quirk list (which should really be
> > made generic) so I can disable it on more 'modern' powerpc as well.
>
> Oh please oh please oh please yes do.
>
> OTOH, it would be even better to just fix up the device tree in the
> early platform code. Quirks are for broken hardware; software, we
> can fix.
That would work if I could bloody remember which machines need what on
what nodes ... some of those are ancient and I don't have access to all
of them.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH v2] lite5200: fix ethernet phy address
From: Dmitry Eremin-Solenikov @ 2010-06-15 22:19 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1276154181-25534-1-git-send-email-dbaryshkov__1637.54533646188$1276154215$gmane$org@gmail.com>
Dmitry Eremin-Solenikov wrote:
> According to my schematics, on Lite5200 board ethernet phy uses address
> 0 (all ADDR lines are pulled down). With this change I can talk to
> onboard phy (LXT971) and correctly use autonegotiation.
What about this patch?
--
With best wishes
Dmitry
^ permalink raw reply
* Porting a driver to powerpc using FDT
From: Chris Alfred (Internode) @ 2010-06-15 22:18 UTC (permalink / raw)
To: linuxppc-dev
I am trying to port a DSA (Distributed Switch Architecture) driver for
the Micrel KS8995M managed switch connected to a MPC5200. There is an
SPI interface and MII interface managed by the DSA driver.
I can't understand how probe gets called when the flatted device tree
(FDT) system is used, and how to bind such a driver using the FDT (if
you have to at all).
The DSA driver is initialised via:
// net/dsa/dsa.c
static struct platform_driver dsa_driver = {
.probe = dsa_probe,
.remove = dsa_remove,
.shutdown = dsa_shutdown,
.driver = {
.name = "dsa",
.owner = THIS_MODULE,
},
};
static int __init dsa_init_module(void)
{
return platform_driver_register(&dsa_driver);
}
dsa_init_module is being called; but how do I get the system to call
.probe?
Chris
^ permalink raw reply
* Porting a driver to powerpc using FDT
From: Chris Alfred @ 2010-06-15 22:19 UTC (permalink / raw)
To: linuxppc-dev
I am trying to port a DSA (Distributed Switch Architecture) driver for
the Micrel KS8995M managed switch connected to a MPC5200. There is an
SPI interface and MII interface managed by the DSA driver.
I can't understand how probe gets called when the flatted device tree
(FDT) system is used, and how to bind such a driver using the FDT (if
you have to at all).
The DSA driver is initialised via:
// net/dsa/dsa.c
static struct platform_driver dsa_driver = {
.probe = dsa_probe,
.remove = dsa_remove,
.shutdown = dsa_shutdown,
.driver = {
.name = "dsa",
.owner = THIS_MODULE,
},
};
static int __init dsa_init_module(void)
{
return platform_driver_register(&dsa_driver);
}
dsa_init_module is being called; but how do I get the system to call
.probe?
Chris
^ permalink raw reply
* [PATCH 1/2 v4] powerpc/5200: add mpc5200_psc_ac97_gpio_reset
From: Eric Millbrandt @ 2010-06-15 21:53 UTC (permalink / raw)
To: Grant Likely; +Cc: Mark Brown, linuxppc-dev, Eric Millbrandt
In-Reply-To: <1276617907-10862-2-git-send-email-emillbrandt@dekaresearch.com>
Work around a silicon bug in the ac97 reset functionality of the
mpc5200(b). The implementation of the ac97 "cold" reset is flawed.
If the sync and output lines are high when reset is asserted the
attached ac97 device may go into test mode. Avoid this by
reconfiguring the psc to gpio mode and generating the reset manually.
>From MPC5200B User's Manual:
"Some AC97 devices goes to a test mode, if the Sync line is high
during the Res line is low (reset phase). To avoid this behavior the
Sync line must be also forced to zero during the reset phase. To do
that, the pin muxing should switch to GPIO mode and the GPIO control
register should be used to control the output lines."
Signed-off-by: Eric Millbrandt <emillbrandt@dekaresearch.com>
---
changes since v1
- Amended with comments from Mark Brown
- Fall back to the original reset implementation if no gpio pins are define=
d
in the device tree
changes since v2
- Refactored to move the port_config manipulation to platform code.
- Remove the gpio pins from the device-tree
changes since v3
- Remove redundant checks around call to mpc5200_psc_ac97_gpio_reset()
changes since v4
- cleanup inverted logic
arch/powerpc/include/asm/mpc52xx.h | 1 +
arch/powerpc/include/asm/mpc52xx_psc.h | 1 +
arch/powerpc/platforms/52xx/mpc52xx_common.c | 111 ++++++++++++++++++++++=
++++
3 files changed, 113 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/=
mpc52xx.h
index b664ce7..1f41382 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -271,6 +271,7 @@ struct mpc52xx_intr {
/* mpc52xx_common.c */
extern void mpc5200_setup_xlb_arbiter(void);
extern void mpc52xx_declare_of_platform_devices(void);
+extern int mpc5200_psc_ac97_gpio_reset(int psc_number);
extern void mpc52xx_map_common_devices(void);
extern int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv);
extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node);
diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/=
asm/mpc52xx_psc.h
index ecc4fc6..2966df6 100644
--- a/arch/powerpc/include/asm/mpc52xx_psc.h
+++ b/arch/powerpc/include/asm/mpc52xx_psc.h
@@ -131,6 +131,7 @@
#define MPC52xx_PSC_SICR_SIM_FIR (0x6 << 24)
#define MPC52xx_PSC_SICR_SIM_CODEC_24 (0x7 << 24)
#define MPC52xx_PSC_SICR_SIM_CODEC_32 (0xf << 24)
+#define MPC52xx_PSC_SICR_ACRB (0x8 << 24)
#define MPC52xx_PSC_SICR_AWR (1 << 30)
#define MPC52xx_PSC_SICR_GENCLK (1 << 23)
#define MPC52xx_PSC_SICR_I2S (1 << 22)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/pl=
atforms/52xx/mpc52xx_common.c
index a46bad0..1887872 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -12,9 +12,11 @@
#undef DEBUG
+#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/mpc52xx.h>
@@ -82,6 +84,14 @@ mpc5200_setup_xlb_arbiter(void)
iounmap(xlb);
}
+/*
+ * This variable is mapped in mpc52xx_map_common_devices and
+ * used in mpc5200_psc_ac97_gpio_reset().
+ */
+static DEFINE_SPINLOCK(gpio_lock);
+struct mpc52xx_gpio __iomem *simple_gpio;
+struct mpc52xx_gpio_wkup __iomem *wkup_gpio;
+
/**
* mpc52xx_declare_of_platform_devices: register internal devices and chil=
dren
* of the localplus bus to the of_plat=
form
@@ -109,6 +119,19 @@ static struct of_device_id mpc52xx_cdm_ids[] __initdat=
a =3D {
{ .compatible =3D "mpc5200-cdm", }, /* old */
{}
};
+static const struct of_device_id mpc52xx_gpio_simple[] =3D {
+ {
+ .compatible =3D "fsl,mpc5200-gpio",
+ },
+ {}
+};
+static const struct of_device_id mpc52xx_gpio_wkup[] =3D {
+ {
+ .compatible =3D "fsl,mpc5200-gpio-wkup",
+ },
+ {}
+};
+
/**
* mpc52xx_map_common_devices: iomap devices required by common code
@@ -135,6 +158,16 @@ mpc52xx_map_common_devices(void)
np =3D of_find_matching_node(NULL, mpc52xx_cdm_ids);
mpc52xx_cdm =3D of_iomap(np, 0);
of_node_put(np);
+
+ /* simple_gpio registers */
+ np =3D of_find_matching_node(NULL, mpc52xx_gpio_simple);
+ simple_gpio =3D of_iomap(np, 0);
+ of_node_put(np);
+
+ /* wkup_gpio registers */
+ np =3D of_find_matching_node(NULL, mpc52xx_gpio_wkup);
+ wkup_gpio =3D of_iomap(np, 0);
+ of_node_put(np);
}
/**
@@ -233,3 +266,81 @@ mpc52xx_restart(char *cmd)
while (1);
}
+
+#define PSC1_RESET 0x1
+#define PSC1_SYNC 0x4
+#define PSC1_SDATA_OUT 0x1
+#define PSC2_RESET 0x2
+#define PSC2_SYNC (0x4<<4)
+#define PSC2_SDATA_OUT (0x1<<4)
+#define MPC52xx_GPIO_PSC1_MASK 0x7
+#define MPC52xx_GPIO_PSC2_MASK (0x7<<4)
+
+/**
+ * mpc5200_psc_ac97_gpio_reset: Use gpio pins to reset the ac97 bus
+ *
+ * @psc: psc number to reset (only psc 1 and 2 support ac97)
+ */
+int mpc5200_psc_ac97_gpio_reset(int psc_number)
+{
+ unsigned long flags;
+ u32 gpio;
+ u32 mux;
+ int out;
+ int reset;
+ int sync;
+
+ if ((!simple_gpio) || (!wkup_gpio))
+ return -ENODEV;
+
+ switch (psc_number) {
+ case 0:
+ reset =3D PSC1_RESET; /* AC97_1_RES */
+ sync =3D PSC1_SYNC; /* AC97_1_SYNC */
+ out =3D PSC1_SDATA_OUT; /* AC97_1_SDATA_OUT */
+ gpio =3D MPC52xx_GPIO_PSC1_MASK;
+ break;
+ case 1:
+ reset =3D PSC2_RESET; /* AC97_2_RES */
+ sync =3D PSC2_SYNC; /* AC97_2_SYNC */
+ out =3D PSC2_SDATA_OUT; /* AC97_2_SDATA_OUT */
+ gpio =3D MPC52xx_GPIO_PSC2_MASK;
+ break;
+ default:
+ printk(KERN_ERR __FILE__ ": "
+ "Unable to determine PSC, no ac97 cold-reset will be =
"
+ "performed\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* Reconfiure pin-muxing to gpio */
+ mux =3D in_be32(&simple_gpio->port_config);
+ out_be32(&simple_gpio->port_config, mux & (~gpio));
+
+ /* enable gpio pins for output */
+ setbits8(&wkup_gpio->wkup_gpioe, reset);
+ setbits32(&simple_gpio->simple_gpioe, sync | out);
+
+ setbits8(&wkup_gpio->wkup_ddr, reset);
+ setbits32(&simple_gpio->simple_ddr, sync | out);
+
+ /* Assert cold reset */
+ clrbits32(&simple_gpio->simple_dvo, sync | out);
+ clrbits8(&wkup_gpio->wkup_dvo, reset);
+
+ /* wait at lease 1 us */
+ udelay(2);
+
+ /* Deassert reset */
+ setbits8(&wkup_gpio->wkup_dvo, reset);
+
+ /* Restore pin-muxing */
+ out_be32(&simple_gpio->port_config, mux);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpc5200_psc_ac97_gpio_reset);
--
-DISCLAIMER: an automatically appended disclaimer may follow. By posting-
-to a public e-mail mailing list I hereby grant permission to distribute-
-and copy this message.-
1.6.3.1
This e-mail and the information, including any attachments, it contains are=
intended to be a confidential communication only to the person or entity t=
o whom it is addressed and may contain information that is privileged. If t=
he reader of this message is not the intended recipient, you are hereby not=
ified that any dissemination, distribution or copying of this communication=
is strictly prohibited. If you have received this communication in error, =
please immediately notify the sender and destroy the original message.
Thank you.
Please consider the environment before printing this email.
^ permalink raw reply related
* Re: [PATCH 08/12] ptp: Added a brand new class driver for ptp clocks.
From: Grant Likely @ 2010-06-15 19:11 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, devicetree-discuss, Thomas Gleixner, linuxppc-dev,
linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <4a030d2bace90f089f2f3f61496b918c6f1dfb52.1276615626.git.richard.cochran@omicron.at>
On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
<richardcochran@gmail.com> 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.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Hi Richard,
Some more comments on this patch...
> ---
> =A0Documentation/ptp/ptp.txt =A0 =A0 =A0 =A0| =A0 95 +++++++
> =A0Documentation/ptp/testptp.c =A0 =A0 =A0| =A0269 ++++++++++++++++++++
> =A0Documentation/ptp/testptp.mk =A0 =A0 | =A0 33 +++
> =A0drivers/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0 =A02 +
> =A0drivers/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =A01 +
> =A0drivers/ptp/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0 26 ++
> =A0drivers/ptp/Makefile =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =A05 +
> =A0drivers/ptp/ptp_clock.c =A0 =A0 =A0 =A0 =A0| =A0514 ++++++++++++++++++=
++++++++++++++++++++
> =A0include/linux/Kbuild =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =A01 +
> =A0include/linux/ptp_clock.h =A0 =A0 =A0 =A0| =A0 79 ++++++
> =A0include/linux/ptp_clock_kernel.h | =A0137 ++++++++++
> =A011 files changed, 1162 insertions(+), 0 deletions(-)
> =A0create mode 100644 Documentation/ptp/ptp.txt
> =A0create mode 100644 Documentation/ptp/testptp.c
> =A0create mode 100644 Documentation/ptp/testptp.mk
> =A0create mode 100644 drivers/ptp/Kconfig
> =A0create mode 100644 drivers/ptp/Makefile
> =A0create mode 100644 drivers/ptp/ptp_clock.c
> =A0create mode 100644 include/linux/ptp_clock.h
> =A0create mode 100644 include/linux/ptp_clock_kernel.h
>
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> new file mode 100644
> index 0000000..b86695c
> --- /dev/null
> +++ b/drivers/ptp/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for PTP 1588 clock support.
> +#
> +
> +obj-$(CONFIG_PTP_1588_CLOCK) =A0 =A0 =A0 =A0 =A0 +=3D ptp_clock.o
> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> new file mode 100644
> index 0000000..4753bf3
> --- /dev/null
> +++ b/drivers/ptp/ptp_clock.c
> @@ -0,0 +1,514 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Partially adapted from the Linux PPS driver.
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/bitops.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/ptp_clock.h>
> +
> +#define PTP_MAX_ALARMS 4
> +#define PTP_MAX_CLOCKS BITS_PER_LONG
> +#define PTP_MAX_TIMESTAMPS 128
> +
> +struct alarm {
> + =A0 =A0 =A0 struct pid *pid;
> + =A0 =A0 =A0 int sig;
> +};
> +
> +struct timestamp_event_queue {
> + =A0 =A0 =A0 struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
> + =A0 =A0 =A0 int head;
> + =A0 =A0 =A0 int tail;
> + =A0 =A0 =A0 int overflow;
> +};
> +
> +struct ptp_clock {
> + =A0 =A0 =A0 struct list_head list;
> + =A0 =A0 =A0 struct cdev cdev;
> + =A0 =A0 =A0 struct device *dev;
> + =A0 =A0 =A0 struct ptp_clock_info *info;
> + =A0 =A0 =A0 dev_t devid;
> + =A0 =A0 =A0 int index; /* index into clocks.map, also the minor number =
*/
> +
> + =A0 =A0 =A0 struct alarm alarm[PTP_MAX_ALARMS];
> + =A0 =A0 =A0 struct mutex alarm_mux; /* one process at a time setting an=
alarm */
> +
> + =A0 =A0 =A0 struct timestamp_event_queue tsevq; /* simple fifo for time=
stamps */
> + =A0 =A0 =A0 struct mutex tsevq_mux; /* one process at a time reading th=
e fifo */
> + =A0 =A0 =A0 wait_queue_head_t tsev_wq;
> +};
> +
> +/* private globals */
> +
> +static const struct file_operations ptp_fops;
> +static dev_t ptp_devt;
> +static struct class *ptp_class;
> +
> +static struct {
> + =A0 =A0 =A0 struct list_head list;
> + =A0 =A0 =A0 DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
> +} clocks;
> +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
Doesn't appear that clocks is manipulated at atomic context. Mutex instead=
?
> +
> +/* time stamp event queue operations */
> +
> +static inline int queue_cnt(struct timestamp_event_queue *q)
> +{
> + =A0 =A0 =A0 int cnt =3D q->tail - q->head;
> + =A0 =A0 =A0 return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
> +}
> +
> +static inline int queue_free(struct timestamp_event_queue *q)
> +{
> + =A0 =A0 =A0 return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
> +}
> +
> +static void enqueue_external_timestamp(struct timestamp_event_queue *que=
ue,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0struct ptp_clock_event *src)
> +{
> + =A0 =A0 =A0 struct ptp_extts_event *dst;
> + =A0 =A0 =A0 u32 remainder;
> +
> + =A0 =A0 =A0 dst =3D &queue->buf[queue->tail];
> +
> + =A0 =A0 =A0 dst->index =3D src->index;
> + =A0 =A0 =A0 dst->ts.tv_sec =3D div_u64_rem(src->timestamp, 1000000000, =
&remainder);
> + =A0 =A0 =A0 dst->ts.tv_nsec =3D remainder;
> +
> + =A0 =A0 =A0 if (!queue_free(queue))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 queue->overflow++;
> +
> + =A0 =A0 =A0 queue->tail =3D (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
> +}
> +
> +/* public interface */
> +
> +struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp;
> + =A0 =A0 =A0 int err =3D 0, index, major =3D MAJOR(ptp_devt);
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 if (info->n_alarm > PTP_MAX_ALARMS)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ERR_PTR(-EINVAL);
Okay, this is my opinion here, and other maintainers may disagree with
me, but the ERR_PTR() pattern is a horrible idea. It is non-obvious
when reading code when the pattern is used, and the compiler will not
catch misinterpretation of the return value for you. Please don't add
new instances of using it. Just return NULL on error and log it with
printk() or pr_error(). Very seldom do I find the actual error code
to be actually useful anyway. Generally callers only care about
whether or not the operation succeeded.
> +
> + =A0 =A0 =A0 /* Find a free clock slot and reserve it. */
> + =A0 =A0 =A0 err =3D -EBUSY;
> + =A0 =A0 =A0 spin_lock_irqsave(&clocks_lock, flags);
> + =A0 =A0 =A0 index =3D find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
> + =A0 =A0 =A0 if (index < PTP_MAX_CLOCKS) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_bit(index, clocks.map);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&clocks_lock, flags)=
;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&clocks_lock, flags)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto no_clock;
> + =A0 =A0 =A0 }
If the spinlock is changed to a mutex that is held for the entire
function call, then the logic here can be simpler.
> +
> + =A0 =A0 =A0 /* Initialize a clock structure. */
> + =A0 =A0 =A0 err =3D -ENOMEM;
> + =A0 =A0 =A0 ptp =3D kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
> + =A0 =A0 =A0 if (ptp =3D=3D NULL)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto no_memory;
> +
> + =A0 =A0 =A0 ptp->info =3D info;
> + =A0 =A0 =A0 ptp->devid =3D MKDEV(major, index);
> + =A0 =A0 =A0 ptp->index =3D index;
> + =A0 =A0 =A0 mutex_init(&ptp->alarm_mux);
> + =A0 =A0 =A0 mutex_init(&ptp->tsevq_mux);
> + =A0 =A0 =A0 init_waitqueue_head(&ptp->tsev_wq);
> +
> + =A0 =A0 =A0 /* Create a new device in our class. */
> + =A0 =A0 =A0 ptp->dev =3D device_create(ptp_class, NULL, ptp->devid, ptp=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"ptp_clo=
ck_%d", ptp->index);
> + =A0 =A0 =A0 if (IS_ERR(ptp->dev))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto no_device;
> +
> + =A0 =A0 =A0 dev_set_drvdata(ptp->dev, ptp);
> +
> + =A0 =A0 =A0 /* Register a character device. */
> + =A0 =A0 =A0 cdev_init(&ptp->cdev, &ptp_fops);
> + =A0 =A0 =A0 ptp->cdev.owner =3D info->owner;
> + =A0 =A0 =A0 err =3D cdev_add(&ptp->cdev, ptp->devid, 1);
> + =A0 =A0 =A0 if (err)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto no_cdev;
> +
> + =A0 =A0 =A0 /* Clock is ready, add it into the list. */
> + =A0 =A0 =A0 spin_lock_irqsave(&clocks_lock, flags);
> + =A0 =A0 =A0 list_add(&ptp->list, &clocks.list);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&clocks_lock, flags);
> +
> + =A0 =A0 =A0 return ptp;
> +
> +no_cdev:
> + =A0 =A0 =A0 device_destroy(ptp_class, ptp->devid);
> +no_device:
> + =A0 =A0 =A0 mutex_destroy(&ptp->alarm_mux);
> + =A0 =A0 =A0 mutex_destroy(&ptp->tsevq_mux);
> + =A0 =A0 =A0 kfree(ptp);
> +no_memory:
> + =A0 =A0 =A0 spin_lock_irqsave(&clocks_lock, flags);
> + =A0 =A0 =A0 clear_bit(index, clocks.map);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&clocks_lock, flags);
> +no_clock:
> + =A0 =A0 =A0 return ERR_PTR(err);
> +}
> +EXPORT_SYMBOL(ptp_clock_register);
> +
> +int ptp_clock_unregister(struct ptp_clock *ptp)
> +{
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 /* Release the clock's resources. */
> + =A0 =A0 =A0 cdev_del(&ptp->cdev);
> + =A0 =A0 =A0 device_destroy(ptp_class, ptp->devid);
> + =A0 =A0 =A0 mutex_destroy(&ptp->alarm_mux);
> + =A0 =A0 =A0 mutex_destroy(&ptp->tsevq_mux);
> +
> + =A0 =A0 =A0 /* Remove the clock from the list. */
> + =A0 =A0 =A0 spin_lock_irqsave(&clocks_lock, flags);
> + =A0 =A0 =A0 list_del(&ptp->list);
> + =A0 =A0 =A0 clear_bit(ptp->index, clocks.map);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&clocks_lock, flags);
> +
> + =A0 =A0 =A0 kfree(ptp);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +EXPORT_SYMBOL(ptp_clock_unregister);
> +
> +void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *even=
t)
> +{
> + =A0 =A0 =A0 switch (event->type) {
> +
> + =A0 =A0 =A0 case PTP_CLOCK_ALARM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 kill_pid(ptp->alarm[event->index].pid,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->alarm[event->index]=
.sig, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_EXTTS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enqueue_external_timestamp(&ptp->tsevq, eve=
nt);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 wake_up_interruptible(&ptp->tsev_wq);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_PPS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +}
> +EXPORT_SYMBOL(ptp_clock_event);
> +
> +/* character device operations */
> +
> +static int ptp_ioctl(struct inode *node, struct file *fp,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned int cmd, unsigned long=
arg)
> +{
> + =A0 =A0 =A0 struct ptp_clock_caps caps;
> + =A0 =A0 =A0 struct ptp_clock_request req;
> + =A0 =A0 =A0 struct ptp_clock_timer timer;
> + =A0 =A0 =A0 struct ptp_clock *ptp =3D fp->private_data;
> + =A0 =A0 =A0 struct ptp_clock_info *ops =3D ptp->info;
> + =A0 =A0 =A0 void *priv =3D ops->priv;
> + =A0 =A0 =A0 struct timespec ts;
> + =A0 =A0 =A0 int flags, index;
> + =A0 =A0 =A0 int err =3D 0;
> +
> + =A0 =A0 =A0 switch (cmd) {
> +
> + =A0 =A0 =A0 case PTP_CLOCK_APIVERS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D put_user(PTP_CLOCK_VERSION, (u32 __=
user *)arg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_ADJFREQ:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!capable(CAP_SYS_TIME))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EPERM;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->adjfreq(priv, arg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_ADJTIME:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!capable(CAP_SYS_TIME))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EPERM;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (copy_from_user(&ts, (void __user *)arg,=
sizeof(ts)))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EFAULT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->adjtime(priv, =
&ts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_GETTIME:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->gettime(priv, &ts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (err)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D copy_to_user((void __user *)arg, &t=
s, sizeof(ts));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_SETTIME:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!capable(CAP_SYS_TIME))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EPERM;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (copy_from_user(&ts, (void __user *)arg,=
sizeof(ts)))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EFAULT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->settime(priv, =
&ts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_GETCAPS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 memset(&caps, 0, sizeof(caps));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 caps.max_adj =3D ptp->info->max_adj;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 caps.n_alarm =3D ptp->info->n_alarm;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 caps.n_ext_ts =3D ptp->info->n_ext_ts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 caps.n_per_out =3D ptp->info->n_per_out;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 caps.pps =3D ptp->info->pps;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D copy_to_user((void __user *)arg, &c=
aps, sizeof(caps));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_GETTIMER:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (copy_from_user(&timer, (void __user *)a=
rg, sizeof(timer))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EFAULT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 index =3D timer.alarm_index;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (index < 0 || index >=3D ptp->info->n_al=
arm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->gettimer(priv, index, &timer.t=
sp);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (err)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D copy_to_user((void __user *)arg, &t=
imer, sizeof(timer));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_CLOCK_SETTIMER:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (copy_from_user(&timer, (void __user *)a=
rg, sizeof(timer))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EFAULT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 index =3D timer.alarm_index;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (index < 0 || index >=3D ptp->info->n_al=
arm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!valid_signal(timer.signum))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flags =3D timer.flags;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (flags & (flags !=3D TIMER_ABSTIME)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mutex_lock_interruptible(&ptp->alarm_mu=
x))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ERESTARTSYS;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ptp->alarm[index].pid)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 put_pid(ptp->alarm[index].p=
id);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp->alarm[index].pid =3D get_pid(task_pid(=
current));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp->alarm[index].sig =3D timer.signum;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->settimer(priv, index, flags, &=
timer.tsp);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mutex_unlock(&ptp->alarm_mux);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case PTP_FEATURE_REQUEST:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (copy_from_user(&req, (void __user *)arg=
, sizeof(req))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EFAULT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (req.type) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case PTP_REQUEST_EXTTS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case PTP_REQUEST_PEROUT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case PTP_REQUEST_PPS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!capable(CAP_SYS_TIME))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EPE=
RM;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (err)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D ops->enable(priv, &req,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.fla=
gs & PTP_ENABLE_FEATURE ? 1 : 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -ENOTTY;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return err;
> +}
> +
> +static int ptp_open(struct inode *inode, struct file *fp)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp;
> + =A0 =A0 =A0 ptp =3D container_of(inode->i_cdev, struct ptp_clock, cdev)=
;
> +
> + =A0 =A0 =A0 fp->private_data =3D ptp;
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static unsigned int ptp_poll(struct file *fp, poll_table *wait)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp =3D fp->private_data;
> +
> + =A0 =A0 =A0 poll_wait(fp, &ptp->tsev_wq, wait);
> +
> + =A0 =A0 =A0 return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
> +}
> +
> +static ssize_t ptp_read(struct file *fp, char __user *buf,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 size_t cnt, loff_t *off)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp =3D fp->private_data;
> + =A0 =A0 =A0 struct timestamp_event_queue *queue =3D &ptp->tsevq;
> + =A0 =A0 =A0 struct ptp_extts_event *event;
> + =A0 =A0 =A0 size_t qcnt;
> +
> + =A0 =A0 =A0 if (mutex_lock_interruptible(&ptp->tsevq_mux))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ERESTARTSYS;
> +
> + =A0 =A0 =A0 cnt =3D cnt / sizeof(struct ptp_extts_event);
> +
> + =A0 =A0 =A0 if (wait_event_interruptible(ptp->tsev_wq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
(qcnt =3D queue_cnt(&ptp->tsevq)))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mutex_unlock(&ptp->tsevq_mux);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ERESTARTSYS;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (cnt > qcnt)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cnt =3D qcnt;
> +
> + =A0 =A0 =A0 event =3D &queue->buf[queue->head];
> +
> + =A0 =A0 =A0 if (copy_to_user(buf, event, cnt * sizeof(struct ptp_extts_=
event))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mutex_unlock(&ptp->tsevq_mux);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EFAULT;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 queue->head =3D (queue->head + cnt) % PTP_MAX_TIMESTAMPS;
> +
> + =A0 =A0 =A0 mutex_unlock(&ptp->tsevq_mux);
> +
> + =A0 =A0 =A0 return cnt * sizeof(struct ptp_extts_event);
> +}
> +
> +static int ptp_release(struct inode *inode, struct file *fp)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp;
> + =A0 =A0 =A0 struct itimerspec ts =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 {0, 0}, {0, 0}
> + =A0 =A0 =A0 };
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 ptp =3D container_of(inode->i_cdev, struct ptp_clock, cdev)=
;
> +
> + =A0 =A0 =A0 for (i =3D 0; i < ptp->info->n_alarm; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ptp->alarm[i].pid) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp->info->settimer(ptp->in=
fo->priv, i, 0, &ts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 put_pid(ptp->alarm[i].pid);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp->alarm[i].pid =3D NULL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static const struct file_operations ptp_fops =3D {
> + =A0 =A0 =A0 .owner =A0 =A0 =A0 =A0 =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 .ioctl =A0 =A0 =A0 =A0 =A0=3D ptp_ioctl,
> + =A0 =A0 =A0 .open =A0 =A0 =A0 =A0 =A0 =3D ptp_open,
> + =A0 =A0 =A0 .poll =A0 =A0 =A0 =A0 =A0 =3D ptp_poll,
> + =A0 =A0 =A0 .read =A0 =A0 =A0 =A0 =A0 =3D ptp_read,
> + =A0 =A0 =A0 .release =A0 =A0 =A0 =A0=3D ptp_release,
> +};
> +
> +/* sysfs */
> +
> +static ssize_t ptp_show_status(struct device *dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct devic=
e_attribute *attr, char *buf)
> +{
> + =A0 =A0 =A0 struct ptp_clock *ptp =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 return sprintf(buf,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"maximum adjustment: =A0%d\n=
"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"programmable alarms: %d\n"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"external timestamps: %d\n"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"periodic outputs: =A0 =A0%d=
\n"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"has pps: =A0 =A0 =A0 =A0 =
=A0 =A0 %d\n"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"device index: =A0 =A0 =A0 =
=A0%d\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->info->max_adj,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->info->n_alarm,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->info->n_ext_ts,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->info->n_per_out,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->info->pps,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ptp->index);
> +}
Don't do this in a sysfs file. Use a debugfs file if you want to
export freeform data like this. sysfs files should contain either
only one value, or a simple list-of-same-type values. Formatted data
is completely out.
> +
> +struct device_attribute ptp_attrs[] =3D {
> + =A0 =A0 =A0 __ATTR(capabilities, S_IRUGO, ptp_show_status, NULL),
> + =A0 =A0 =A0 __ATTR_NULL,
> +};
> +
> +/* module operations */
> +
> +static void __exit ptp_exit(void)
> +{
> + =A0 =A0 =A0 class_destroy(ptp_class);
> + =A0 =A0 =A0 unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
> +}
> +
> +static int __init ptp_init(void)
> +{
> + =A0 =A0 =A0 int err;
> +
> + =A0 =A0 =A0 INIT_LIST_HEAD(&clocks.list);
> +
> + =A0 =A0 =A0 ptp_class =3D class_create(THIS_MODULE, "ptp");
> + =A0 =A0 =A0 if (!ptp_class) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "ptp: failed to allocate cl=
ass\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 ptp_class->dev_attrs =3D ptp_attrs;
> +
> + =A0 =A0 =A0 err =3D alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "=
ptp");
> + =A0 =A0 =A0 if (err < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "ptp: failed to allocate ch=
ar device region\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto no_region;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 pr_info("PTP clock support registered\n");
> + =A0 =A0 =A0 return 0;
> +
> +no_region:
> + =A0 =A0 =A0 class_destroy(ptp_class);
> + =A0 =A0 =A0 return err;
> +}
> +
> +subsys_initcall(ptp_init);
> +module_exit(ptp_exit);
> +
> +MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
> +MODULE_DESCRIPTION("PTP clocks support");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
> index 2fc8e14..9959fe4 100644
> --- a/include/linux/Kbuild
> +++ b/include/linux/Kbuild
> @@ -140,6 +140,7 @@ header-y +=3D pkt_sched.h
> =A0header-y +=3D posix_types.h
> =A0header-y +=3D ppdev.h
> =A0header-y +=3D prctl.h
> +header-y +=3D ptp_clock.h
> =A0header-y +=3D qnxtypes.h
> =A0header-y +=3D qnx4_fs.h
> =A0header-y +=3D radeonfb.h
> diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
> new file mode 100644
> index 0000000..5a509c5
> --- /dev/null
> +++ b/include/linux/ptp_clock.h
> @@ -0,0 +1,79 @@
> +/*
> + * PTP 1588 clock support - user space interface
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_H_
> +#define _PTP_CLOCK_H_
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +#define PTP_ENABLE_FEATURE (1<<0)
> +#define PTP_RISING_EDGE =A0 =A0(1<<1)
> +#define PTP_FALLING_EDGE =A0 (1<<2)
> +
> +enum ptp_request_types {
> + =A0 =A0 =A0 PTP_REQUEST_EXTTS,
> + =A0 =A0 =A0 PTP_REQUEST_PEROUT,
> + =A0 =A0 =A0 PTP_REQUEST_PPS,
> +};
> +
> +struct ptp_clock_caps {
> + =A0 =A0 =A0 __s32 max_adj; /* Maximum frequency adjustment, parts per b=
illon. */
> + =A0 =A0 =A0 int n_alarm; =A0 /* Number of programmable alarms. */
> + =A0 =A0 =A0 int n_ext_ts; =A0/* Number of external time stamp channels.=
*/
> + =A0 =A0 =A0 int n_per_out; /* Number of programmable periodic signals. =
*/
> + =A0 =A0 =A0 int pps; =A0 =A0 =A0 /* Whether the clock supports a PPS ca=
llback. */
> +};
> +
> +struct ptp_clock_timer {
> + =A0 =A0 =A0 int alarm_index; =A0 =A0 =A0 /* Which alarm to query or con=
figure. */
> + =A0 =A0 =A0 int signum; =A0 =A0 =A0 =A0 =A0 =A0/* Requested signal. */
> + =A0 =A0 =A0 int flags; =A0 =A0 =A0 =A0 =A0 =A0 /* Zero or TIMER_ABSTIME=
, see TIMER_SETTIME(2) */
> + =A0 =A0 =A0 struct itimerspec tsp; /* See TIMER_SETTIME(2) */
> +};
> +
> +struct ptp_clock_request {
> + =A0 =A0 =A0 int type; =A0/* One of the ptp_request_types enumeration va=
lues. */
> + =A0 =A0 =A0 int index; /* Which channel to configure. */
> + =A0 =A0 =A0 struct timespec ts; /* For period signals, the desired peri=
od. */
> + =A0 =A0 =A0 int flags; /* Bit field for PTP_ENABLE_FEATURE or other fla=
gs. */
> +};
> +
> +struct ptp_extts_event {
> + =A0 =A0 =A0 int index;
> + =A0 =A0 =A0 struct timespec ts;
> +};
> +
> +#define PTP_CLOCK_VERSION 0x00000001
> +
> +#define PTP_CLK_MAGIC '=3D'
> +
> +#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)
> +#define PTP_CLOCK_ADJFREQ _IO =A0(PTP_CLK_MAGIC, 2)
> +#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)
> +
> +#define PTP_CLOCK_GETCAPS =A0 _IOR =A0(PTP_CLK_MAGIC, 6, struct ptp_cloc=
k_caps)
> +#define PTP_CLOCK_GETTIMER =A0_IOWR (PTP_CLK_MAGIC, 7, struct ptp_clock_=
timer)
> +#define PTP_CLOCK_SETTIMER =A0_IOW =A0(PTP_CLK_MAGIC, 8, struct ptp_cloc=
k_timer)
> +#define PTP_FEATURE_REQUEST _IOW =A0(PTP_CLK_MAGIC, 9, struct ptp_clock_=
request)
> +
> +#endif
> diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_k=
ernel.h
> new file mode 100644
> index 0000000..d6cc158
> --- /dev/null
> +++ b/include/linux/ptp_clock_kernel.h
> @@ -0,0 +1,137 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_KERNEL_H_
> +#define _PTP_CLOCK_KERNEL_H_
> +
> +#include <linux/ptp_clock.h>
> +
> +/**
> + * struct ptp_clock_info - decribes a PTP hardware clock
> + *
> + * @owner: =A0 =A0 The clock driver should set to THIS_MODULE.
> + * @name: =A0 =A0 =A0A short name to identify the clock.
> + * @max_adj: =A0 The maximum possible frequency adjustment, in parts per=
billon.
> + * @n_alarm: =A0 The number of programmable alarms.
> + * @n_ext_ts: =A0The number of external time stamp channels.
> + * @n_per_out: The number of programmable periodic signals.
> + * @pps: =A0 =A0 =A0 Indicates whether the clock supports a PPS callback=
.
> + * @priv: =A0 =A0 =A0Passed to the clock operations, for the driver's pr=
ivate use.
> + *
> + * clock operations
> + *
> + * @adjfreq: =A0Adjusts the frequency of the hardware clock.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter delta: Desired period change in part=
s per billion.
> + *
> + * @adjtime: =A0Shifts the time of the hardware clock.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter ts: Desired change in seconds and na=
noseconds.
> + *
> + * @gettime: =A0Reads the current time from the hardware clock.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter ts: Holds the result.
> + *
> + * @settime: =A0Set the current time on the hardware clock.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter ts: Time value to set.
> + *
> + * @gettimer: Reads the time remaining from the given timer.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter index: Which alarm to query.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter ts: Holds the result.
> + *
> + * @settimer: Arms the given timer for periodic or one shot operation.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter index: Which alarm to set.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter abs: TIMER_ABSTIME, or zero for rela=
tive timer.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter ts: Alarm time and period to set.
> + *
> + * @enable: =A0 Request driver to enable or disable an ancillary feature=
.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter request: Desired resource to enable =
or disable.
> + * =A0 =A0 =A0 =A0 =A0 =A0parameter on: Caller passes one to enable or z=
ero to disable.
> + *
> + * The callbacks must all return zero on success, non-zero otherwise.
> + */
> +
> +struct ptp_clock_info {
> + =A0 =A0 =A0 struct module *owner;
> + =A0 =A0 =A0 char name[16];
> + =A0 =A0 =A0 s32 max_adj;
> + =A0 =A0 =A0 int n_alarm;
> + =A0 =A0 =A0 int n_ext_ts;
> + =A0 =A0 =A0 int n_per_out;
> + =A0 =A0 =A0 int pps;
> + =A0 =A0 =A0 void *priv;
> + =A0 =A0 =A0 int (*adjfreq)(void *priv, s32 delta);
> + =A0 =A0 =A0 int (*adjtime)(void *priv, struct timespec *ts);
> + =A0 =A0 =A0 int (*gettime)(void *priv, struct timespec *ts);
> + =A0 =A0 =A0 int (*settime)(void *priv, struct timespec *ts);
> + =A0 =A0 =A0 int (*gettimer)(void *priv, int index, struct itimerspec *t=
s);
> + =A0 =A0 =A0 int (*settimer)(void *priv, int index, int abs, struct itim=
erspec *ts);
> + =A0 =A0 =A0 int (*enable)(void *priv, struct ptp_clock_request *request=
, int on);
> +};
> +
> +struct ptp_clock;
> +
> +/**
> + * ptp_clock_register() - register a PTP hardware clock driver
> + *
> + * @info: =A0Structure describing the new clock.
> + */
> +
> +extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)=
;
> +
> +/**
> + * ptp_clock_unregister() - unregister a PTP hardware clock driver
> + *
> + * @ptp: =A0The clock to remove from service.
> + */
> +
> +extern int ptp_clock_unregister(struct ptp_clock *ptp);
> +
> +
> +enum ptp_clock_events {
> + =A0 =A0 =A0 PTP_CLOCK_ALARM,
> + =A0 =A0 =A0 PTP_CLOCK_EXTTS,
> + =A0 =A0 =A0 PTP_CLOCK_PPS,
> +};
> +
> +/**
> + * struct ptp_clock_event - decribes a PTP hardware clock event
> + *
> + * @type: =A0One of the ptp_clock_events enumeration values.
> + * @index: Identifies the source of the event.
> + * @timestamp: When the event occured.
> + */
> +
> +struct ptp_clock_event {
> + =A0 =A0 =A0 int type;
> + =A0 =A0 =A0 int index;
> + =A0 =A0 =A0 u64 timestamp;
> +};
> +
> +/**
> + * ptp_clock_event() - notify the PTP layer about an event
> + *
> + * This function should only be called from interrupt context.
> + *
> + * @ptp: =A0 =A0The clock obtained from ptp_clock_register().
> + * @event: =A0Message structure describing the event.
> + */
> +
> +extern void ptp_clock_event(struct ptp_clock *ptp,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct ptp_clock_ev=
ent *event);
> +
> +#endif
> --
> 1.6.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 12/12] ptp: Added a clock driver for the National Semiconductor PHYTER.
From: Grant Likely @ 2010-06-15 18:49 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <37f5015b186bdd51f8451ade042f90c8b39a5cc8.1276615626.git.richard.cochran@omicron.at>
On Tue, Jun 15, 2010 at 10:10 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> This patch adds support for the PTP clock found on the DP83640. Only the
> basic clock operations have been implemented.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
> ---
> =A0drivers/net/phy/Kconfig =A0 | =A0 11 +++
> =A0drivers/net/phy/dp83640.c | =A0158 +++++++++++++++++++++++++++++++++++=
+++++++++-
> =A02 files changed, 168 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 430cab1..507c68a 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -79,9 +79,20 @@ config NATIONAL_PHY
>
> =A0config DP83640_PHY
> =A0 =A0 =A0 =A0tristate "Driver for the National Semiconductor DP83640 PH=
YTER"
> + =A0 =A0 =A0 depends on PTP_1588_CLOCK
> + =A0 =A0 =A0 depends on NETWORK_PHY_TIMESTAMPING
Won't this break things for existing DP83640 users?
> =A0 =A0 =A0 =A0---help---
> =A0 =A0 =A0 =A0 =A0Supports the DP83640 PHYTER with IEEE 1588 features.
>
> + =A0 =A0 =A0 =A0 This driver adds support for using the DP83640 as a PTP
> + =A0 =A0 =A0 =A0 clock. This clock is only useful if your PTP programs a=
re
> + =A0 =A0 =A0 =A0 getting hardware time stamps on the PTP Ethernet packet=
s
> + =A0 =A0 =A0 =A0 using the SO_TIMESTAMPING API.
> +
> + =A0 =A0 =A0 =A0 In order for this to work, your MAC driver must also
> + =A0 =A0 =A0 =A0 implement the the skb_tx_timetamp() and skb_rx_timetamp=
()
> + =A0 =A0 =A0 =A0 functions.
> +
> =A0config STE10XP
> =A0 =A0 =A0 =A0depends on PHYLIB
> =A0 =A0 =A0 =A0tristate "Driver for STMicroelectronics STe10Xp PHYs"
> diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
> index a3217ea..21eadc3 100644
> --- a/drivers/net/phy/dp83640.c
> +++ b/drivers/net/phy/dp83640.c
> @@ -26,6 +26,7 @@
> =A0#include <linux/netdevice.h>
> =A0#include <linux/phy.h>
> =A0#include <linux/ptp_classify.h>
> +#include <linux/ptp_clock_kernel.h>
>
> =A0#include "dp83640_reg.h"
>
> @@ -45,10 +46,13 @@ struct rxts {
> =A0};
>
> =A0struct dp83640_private {
> + =A0 =A0 =A0 struct phy_device *phydev;
> =A0 =A0 =A0 =A0int hwts_tx_en;
> =A0 =A0 =A0 =A0int hwts_rx_en;
> =A0 =A0 =A0 =A0int layer;
> =A0 =A0 =A0 =A0int version;
> + =A0 =A0 =A0 /* protects PTP_TDR register from concurrent access */
> + =A0 =A0 =A0 spinlock_t ptp_tdr_lock;
> =A0 =A0 =A0 =A0/* protects extended registers from concurrent access */
> =A0 =A0 =A0 =A0spinlock_t extreg_lock;
> =A0 =A0 =A0 =A0int page;
> @@ -60,6 +64,9 @@ struct dp83640_private {
>
> =A0/* globals */
>
> +static struct ptp_clock *dp83640_clock;
> +DEFINE_SPINLOCK(clock_lock); /* protects the one and only dp83640_clock =
*/
Why only one? Is it not possible to have 2 of these PHYs in a system?
> +
> =A0static struct sock_filter ptp_filter[] =3D {
> =A0 =A0 =A0 =A0PTP_FILTER
> =A0};
> @@ -99,6 +106,129 @@ static void ext_write(struct phy_device *phydev, int=
page, u32 regnum, u16 val)
> =A0 =A0 =A0 =A0spin_unlock(&dp83640->extreg_lock);
> =A0}
>
> +static int tdr_write(struct dp83640_private *dp83640,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct timespec *ts, u16 cmd)
> +{
> + =A0 =A0 =A0 struct phy_device *phydev =3D dp83640->phydev;
> +
> + =A0 =A0 =A0 spin_lock(&dp83640->ptp_tdr_lock);
> +
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* =
ns[15:0] */
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec >> 16); =A0 /=
* ns[31:16] */
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* =
sec[15:0] */
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec >> 16); =A0 =
=A0/* sec[31:16] */
> +
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_CTL, cmd);
> +
> + =A0 =A0 =A0 spin_unlock(&dp83640->ptp_tdr_lock);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/* ptp clock methods */
> +
> +static int ptp_dp83640_adjfreq(void *priv, s32 ppb)
> +{
> + =A0 =A0 =A0 struct dp83640_private *dp83640 =3D priv;
> + =A0 =A0 =A0 struct phy_device *phydev =3D dp83640->phydev;
> + =A0 =A0 =A0 u64 rate;
> + =A0 =A0 =A0 int neg_adj =3D 0;
> + =A0 =A0 =A0 u16 hi, lo;
> +
> + =A0 =A0 =A0 if (!ppb)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (ppb < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 neg_adj =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ppb =3D -ppb;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 rate =3D ppb;
> + =A0 =A0 =A0 rate <<=3D 26;
> + =A0 =A0 =A0 rate =3D div_u64(rate, 1953125);
> +
> + =A0 =A0 =A0 hi =3D (rate >> 16) & PTP_RATE_HI_MASK;
> + =A0 =A0 =A0 if (neg_adj)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hi |=3D PTP_RATE_DIR;
> +
> + =A0 =A0 =A0 lo =3D rate & 0xffff;
> +
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_RATEH, hi);
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_RATEL, lo);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_dp83640_adjtime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 return tdr_write(priv, ts, PTP_STEP_CLK);
> +}
> +
> +static int ptp_dp83640_gettime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 struct dp83640_private *dp83640 =3D priv;
> + =A0 =A0 =A0 struct phy_device *phydev =3D dp83640->phydev;
> + =A0 =A0 =A0 unsigned int val[4];
> +
> + =A0 =A0 =A0 spin_lock(&dp83640->ptp_tdr_lock);
> +
> + =A0 =A0 =A0 ext_write(phydev, PAGE4, PTP_CTL, PTP_RD_CLK);
> +
> + =A0 =A0 =A0 val[0] =3D ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */
> + =A0 =A0 =A0 val[1] =3D ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] *=
/
> + =A0 =A0 =A0 val[2] =3D ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] *=
/
> + =A0 =A0 =A0 val[3] =3D ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] =
*/
> +
> + =A0 =A0 =A0 spin_unlock(&dp83640->ptp_tdr_lock);
> +
> + =A0 =A0 =A0 ts->tv_nsec =3D val[0] | (val[1] << 16);
> + =A0 =A0 =A0 ts->tv_sec =A0=3D val[2] | (val[3] << 16);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_dp83640_settime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 return tdr_write(priv, ts, PTP_LOAD_CLK);
> +}
> +
> +static int ptp_dp83640_gettimer(void *priv, int index, struct itimerspec=
*ts)
> +{
> + =A0 =A0 =A0 /* We do not (yet) offer any ancillary features. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static int ptp_dp83640_settimer(void *p, int i, int abs, struct itimersp=
ec *ts)
> +{
> + =A0 =A0 =A0 /* We do not (yet) offer any ancillary features. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static int ptp_dp83640_enable(void *priv, struct ptp_clock_request *rq, =
int on)
> +{
> + =A0 =A0 =A0 /* We do not (yet) offer any ancillary features. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static struct ptp_clock_info ptp_dp83640_caps =3D {
> + =A0 =A0 =A0 .owner =A0 =A0 =A0 =A0 =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "dp83640 timer",
> + =A0 =A0 =A0 .max_adj =A0 =A0 =A0 =A0=3D 1953124,
> + =A0 =A0 =A0 .n_alarm =A0 =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .n_ext_ts =A0 =A0 =A0 =3D 0,
> + =A0 =A0 =A0 .n_per_out =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .pps =A0 =A0 =A0 =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .priv =A0 =A0 =A0 =A0 =A0 =3D NULL,
ditto here, can leave the 0s and nulls out.
> + =A0 =A0 =A0 .adjfreq =A0 =A0 =A0 =A0=3D ptp_dp83640_adjfreq,
> + =A0 =A0 =A0 .adjtime =A0 =A0 =A0 =A0=3D ptp_dp83640_adjtime,
> + =A0 =A0 =A0 .gettime =A0 =A0 =A0 =A0=3D ptp_dp83640_gettime,
> + =A0 =A0 =A0 .settime =A0 =A0 =A0 =A0=3D ptp_dp83640_settime,
> + =A0 =A0 =A0 .gettimer =A0 =A0 =A0 =3D ptp_dp83640_gettimer,
> + =A0 =A0 =A0 .settimer =A0 =A0 =A0 =3D ptp_dp83640_settimer,
> + =A0 =A0 =A0 .enable =A0 =A0 =A0 =A0 =3D ptp_dp83640_enable,
> +};
> +
> +/* time stamping methods */
> +
> =A0static int expired(struct rxts *rxts)
> =A0{
> =A0 =A0 =A0 =A0return time_after(jiffies, rxts->tmo);
> @@ -144,6 +274,7 @@ static int match(struct sk_buff *skb, unsigned int ty=
pe, struct rxts *rxts)
> =A0static int dp83640_probe(struct phy_device *phydev)
> =A0{
> =A0 =A0 =A0 =A0struct dp83640_private *dp83640;
> + =A0 =A0 =A0 unsigned long flags;
> =A0 =A0 =A0 =A0int i;
>
> =A0 =A0 =A0 =A0if (sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter))) {
> @@ -155,8 +286,9 @@ static int dp83640_probe(struct phy_device *phydev)
> =A0 =A0 =A0 =A0if (!dp83640)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -ENOMEM;
>
> + =A0 =A0 =A0 dp83640->phydev =3D phydev;
> + =A0 =A0 =A0 spin_lock_init(&dp83640->ptp_tdr_lock);
> =A0 =A0 =A0 =A0spin_lock_init(&dp83640->extreg_lock);
> -
> =A0 =A0 =A0 =A0INIT_LIST_HEAD(&dp83640->rxts);
> =A0 =A0 =A0 =A0INIT_LIST_HEAD(&dp83640->pool);
>
> @@ -165,12 +297,36 @@ static int dp83640_probe(struct phy_device *phydev)
>
> =A0 =A0 =A0 =A0phydev->priv =3D dp83640;
>
> + =A0 =A0 =A0 spin_lock_irqsave(&clock_lock, flags);
> +
> + =A0 =A0 =A0 if (!dp83640_clock) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_dp83640_caps.priv =3D dp83640;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dp83640_clock =3D ptp_clock_register(&ptp_d=
p83640_caps);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (IS_ERR(dp83640_clock)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&clo=
ck_lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 kfree(dp83640);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return PTR_ERR(dp83640_cloc=
k);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 spin_unlock_irqrestore(&clock_lock, flags);
> +
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> =A0static void dp83640_remove(struct phy_device *phydev)
> =A0{
> =A0 =A0 =A0 =A0struct dp83640_private *dp83640 =3D phydev->priv;
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&clock_lock, flags);
> +
> + =A0 =A0 =A0 if (ptp_dp83640_caps.priv =3D=3D dp83640) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock_unregister(dp83640_clock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dp83640_clock =3D NULL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_dp83640_caps.priv =3D NULL;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 spin_unlock_irqrestore(&clock_lock, flags);
> +
> =A0 =A0 =A0 =A0kfree(dp83640);
> =A0}
>
> --
> 1.6.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 11/12] ptp: Added a clock driver for the IXP46x.
From: Grant Likely @ 2010-06-15 18:41 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <0c03ef3e283c6b3ef58feaf1f77ccb0fd605010b.1276615626.git.richard.cochran@omicron.at>
On Tue, Jun 15, 2010 at 10:10 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> This patch adds a driver for the hardware time stamping unit found on the
> IXP465. Only the basic clock operations are implemented.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Hi Richard,
Comments below...
> ---
> =A0arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h | =A0 67 +++++++
> =A0drivers/net/arm/ixp4xx_eth.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A01=
94 +++++++++++++++++++++
> =A0drivers/ptp/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 | =A0 13 ++
> =A0drivers/ptp/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0| =A0 =A01 +
> =A0drivers/ptp/ptp_ixp46x.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =
=A0231 +++++++++++++++++++++++++
> =A05 files changed, 506 insertions(+), 0 deletions(-)
> =A0create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
> =A0create mode 100644 drivers/ptp/ptp_ixp46x.c
>
> diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mac=
h-ixp4xx/include/mach/ixp46x_ts.h
> new file mode 100644
> index 0000000..7fb02b6
> --- /dev/null
> +++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
> @@ -0,0 +1,67 @@
> +/*
> + * PTP 1588 clock using the IXP46X
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, 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 {
> + =A0 =A0 =A0 u32 Ch_Control; /* 0x40 Time Synchronization Channel Contro=
l */
> + =A0 =A0 =A0 u32 Ch_Event; =A0 /* 0x44 Time Synchronization Channel Even=
t */
> + =A0 =A0 =A0 u32 TxSnapLo; =A0 /* 0x48 Transmit Snapshot Low Register */
> + =A0 =A0 =A0 u32 TxSnapHi; =A0 /* 0x4C Transmit Snapshot High Register *=
/
> + =A0 =A0 =A0 u32 RxSnapLo; =A0 /* 0x50 Receive Snapshot Low Register */
> + =A0 =A0 =A0 u32 RxSnapHi; =A0 /* 0x54 Receive Snapshot High Register */
> + =A0 =A0 =A0 u32 SrcUUIDLo; =A0/* 0x58 Source UUID0 Low Register */
> + =A0 =A0 =A0 u32 SrcUUIDHi; =A0/* 0x5C Sequence Identifier/Source UUID0 =
High */
> +};
Nitpick. We use all lower case names for structures in Linux.
> +
> +struct ixp46x_ts_regs {
> + =A0 =A0 =A0 u32 Control; =A0 =A0 /* 0x00 Time Sync Control Register */
> + =A0 =A0 =A0 u32 Event; =A0 =A0 =A0 /* 0x04 Time Sync Event Register */
> + =A0 =A0 =A0 u32 Addend; =A0 =A0 =A0/* 0x08 Time Sync Addend Register */
> + =A0 =A0 =A0 u32 Accum; =A0 =A0 =A0 /* 0x0C Time Sync Accumulator Regist=
er */
> + =A0 =A0 =A0 u32 Test; =A0 =A0 =A0 =A0/* 0x10 Time Sync Test Register */
> + =A0 =A0 =A0 u32 Unused; =A0 =A0 =A0/* 0x14 */
> + =A0 =A0 =A0 u32 RSysTime_Lo; /* 0x18 RawSystemTime_Low Register */
> + =A0 =A0 =A0 u32 RSysTimeHi; =A0/* 0x1C RawSystemTime_High Register */
> + =A0 =A0 =A0 u32 SysTimeLo; =A0 /* 0x20 SystemTime_Low Register */
> + =A0 =A0 =A0 u32 SysTimeHi; =A0 /* 0x24 SystemTime_High Register */
> + =A0 =A0 =A0 u32 TrgtLo; =A0 =A0 =A0/* 0x28 TargetTime_Low Register */
> + =A0 =A0 =A0 u32 TrgtHi; =A0 =A0 =A0/* 0x2C TargetTime_High Register */
> + =A0 =A0 =A0 u32 ASMSLo; =A0 =A0 =A0/* 0x30 Auxiliary Slave Mode Snapsho=
t Low =A0*/
> + =A0 =A0 =A0 u32 ASMSHi; =A0 =A0 =A0/* 0x34 Auxiliary Slave Mode Snapsho=
t High */
> + =A0 =A0 =A0 u32 AMMSLo; =A0 =A0 =A0/* 0x38 Auxiliary Master Mode Snapsh=
ot Low */
> + =A0 =A0 =A0 u32 AMMSHi; =A0 =A0 =A0/* 0x3C Auxiliary Master Mode Snapsh=
ot High */
> +
> + =A0 =A0 =A0 struct ixp46x_channel_ctl channel[3];
> +};
> +
> +/* 0x40 Time Synchronization Channel Control Register Bits */
> +#define MASTER_MODE =A0 (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..2201960 100644
> --- a/drivers/net/arm/ixp4xx_eth.c
> +++ b/drivers/net/arm/ixp4xx_eth.c
> @@ -30,9 +30,12 @@
> =A0#include <linux/etherdevice.h>
> =A0#include <linux/io.h>
> =A0#include <linux/kernel.h>
> +#include <linux/net_tstamp.h>
> =A0#include <linux/phy.h>
> =A0#include <linux/platform_device.h>
> +#include <linux/ptp_classify.h>
> =A0#include <linux/slab.h>
> +#include <mach/ixp46x_ts.h>
> =A0#include <mach/npe.h>
> =A0#include <mach/qmgr.h>
>
> @@ -67,6 +70,14 @@
> =A0#define RXFREE_QUEUE(port_id) =A0(NPE_ID(port_id) + 26)
> =A0#define TXDONE_QUEUE =A0 =A0 =A0 =A0 =A0 31
>
> +#define PTP_SLAVE_MODE =A0 =A0 =A0 =A0 1
> +#define PTP_MASTER_MODE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A02
> +#define PORT2CHANNEL(p) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01
> +/*
> + * PHYSICAL_ID(p->id) ?
> + * TODO - Figure out correct mapping.
> + */
> +
> =A0/* TX Control Registers */
> =A0#define TX_CNTRL0_TX_EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x01
> =A0#define TX_CNTRL0_HALFDUPLEX =A0 0x02
> @@ -171,6 +182,8 @@ struct port {
> =A0 =A0 =A0 =A0int id; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* logical port ID=
*/
> =A0 =A0 =A0 =A0int speed, duplex;
> =A0 =A0 =A0 =A0u8 firmware[4];
> + =A0 =A0 =A0 int hwts_tx_en;
> + =A0 =A0 =A0 int hwts_rx_en;
> =A0};
>
> =A0/* NPE message structure */
> @@ -246,6 +259,170 @@ static int ports_open;
> =A0static struct port *npe_port_tab[MAX_NPES];
> =A0static struct dma_pool *dma_pool;
>
> +static struct sock_filter ptp_filter[] =3D {
> + =A0 =A0 =A0 PTP_FILTER
> +};
> +
> +static int match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seq)
> +{
> + =A0 =A0 =A0 unsigned int type;
> + =A0 =A0 =A0 u16 *hi, *id;
> + =A0 =A0 =A0 u8 *lo, *data =3D skb->data;
> +
> + =A0 =A0 =A0 type =3D sk_run_filter(skb, ptp_filter, ARRAY_SIZE(ptp_filt=
er));
> +
> + =A0 =A0 =A0 if (PTP_CLASS_V1_IPV4 =3D=3D type) {
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 id =3D (u16 *)(data + 42 + 30);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hi =3D (u16 *)(data + 42 + 22);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 lo =3D data + 42 + 24;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return (uid_hi =3D=3D *hi &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0 =3D=3D memcmp(&uid_lo, lo=
, sizeof(uid_lo)) &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 seq =3D=3D *id);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static void do_rx_timestamp(struct port *port, struct sk_buff *skb)
> +{
> + =A0 =A0 =A0 struct skb_shared_hwtstamps *shhwtstamps;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs;
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 ch, hi, lo, val;
> + =A0 =A0 =A0 u16 uid, seq;
> +
> + =A0 =A0 =A0 if (!port->hwts_rx_en)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> +
> + =A0 =A0 =A0 ch =3D PORT2CHANNEL(port);
> +
> + =A0 =A0 =A0 regs =3D (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_=
BASE_VIRT;
> +
> + =A0 =A0 =A0 val =3D __raw_readl(®s->channel[ch].Ch_Event);
> +
> + =A0 =A0 =A0 if (!(val & RX_SNAPSHOT_LOCKED))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> +
> + =A0 =A0 =A0 lo =3D __raw_readl(®s->channel[ch].SrcUUIDLo);
> + =A0 =A0 =A0 hi =3D __raw_readl(®s->channel[ch].SrcUUIDHi);
> +
> + =A0 =A0 =A0 uid =3D hi & 0xffff;
> + =A0 =A0 =A0 seq =3D (hi >> 16) & 0xffff;
> +
> + =A0 =A0 =A0 if (!match(skb, htons(uid), htonl(lo), htons(seq)))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
> +
> + =A0 =A0 =A0 lo =3D __raw_readl(®s->channel[ch].RxSnapLo);
> + =A0 =A0 =A0 hi =3D __raw_readl(®s->channel[ch].RxSnapHi);
> + =A0 =A0 =A0 ns =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 ns |=3D lo;
> + =A0 =A0 =A0 ns <<=3D TICKS_NS_SHIFT;
> +
> + =A0 =A0 =A0 shhwtstamps =3D skb_hwtstamps(skb);
> + =A0 =A0 =A0 memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> + =A0 =A0 =A0 shhwtstamps->hwtstamp =3D ns_to_ktime(ns);
> +out:
> + =A0 =A0 =A0 __raw_writel(RX_SNAPSHOT_LOCKED, ®s->channel[ch].Ch_Even=
t);
> +}
> +
> +static void do_tx_timestamp(struct port *port, struct sk_buff *skb)
> +{
> +#ifdef __ARMEB__
> + =A0 =A0 =A0 struct skb_shared_hwtstamps shhwtstamps;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs;
> + =A0 =A0 =A0 union skb_shared_tx *shtx;
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 ch, cnt, hi, lo, val;
> +
> + =A0 =A0 =A0 shtx =3D skb_tx(skb);
> +
> + =A0 =A0 =A0 if (!shtx->in_progress)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> +
> + =A0 =A0 =A0 ch =3D PORT2CHANNEL(port);
> +
> + =A0 =A0 =A0 regs =3D (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_=
BASE_VIRT;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* This really stinks, but we have to poll for the Tx tim=
e stamp.
> + =A0 =A0 =A0 =A0* Usually, the time stamp is ready after 4 to 6 microsec=
onds.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 for (cnt =3D 0; cnt < 100; cnt++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val =3D __raw_readl(®s->channel[ch].Ch_E=
vent);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val & TX_SNAPSHOT_LOCKED)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 udelay(1);
You want to get stuff as fast as possible, but there is a udelay()
that just chews up CPU time. Would cpu_relax() be sufficient with a
time-based exit condition in the loop?
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 if (!(val & TX_SNAPSHOT_LOCKED)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 shtx->in_progress =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 lo =3D __raw_readl(®s->channel[ch].TxSnapLo);
> + =A0 =A0 =A0 hi =3D __raw_readl(®s->channel[ch].TxSnapHi);
> + =A0 =A0 =A0 ns =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 ns |=3D lo;
> + =A0 =A0 =A0 ns <<=3D TICKS_NS_SHIFT;
> +
> + =A0 =A0 =A0 memset(&shhwtstamps, 0, sizeof(shhwtstamps));
> + =A0 =A0 =A0 shhwtstamps.hwtstamp =3D ns_to_ktime(ns);
> + =A0 =A0 =A0 skb_tstamp_tx(skb, &shhwtstamps);
> +
> + =A0 =A0 =A0 __raw_writel(TX_SNAPSHOT_LOCKED, ®s->channel[ch].Ch_Even=
t);
> +#endif
> +}
> +
> +static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, =
int cmd)
> +{
> + =A0 =A0 =A0 struct hwtstamp_config cfg;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs;
> + =A0 =A0 =A0 struct port *port =3D netdev_priv(netdev);
> + =A0 =A0 =A0 int ch;
> +
> + =A0 =A0 =A0 if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EFAULT;
> +
> + =A0 =A0 =A0 if (cfg.flags) /* reserved for future extensions */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> +
> + =A0 =A0 =A0 ch =3D PORT2CHANNEL(port);
> + =A0 =A0 =A0 regs =3D (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_=
BASE_VIRT;
> +
> + =A0 =A0 =A0 switch (cfg.tx_type) {
> + =A0 =A0 =A0 case HWTSTAMP_TX_OFF:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 port->hwts_tx_en =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case HWTSTAMP_TX_ON:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 port->hwts_tx_en =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ERANGE;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 switch (cfg.rx_filter) {
> + =A0 =A0 =A0 case HWTSTAMP_FILTER_NONE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 port->hwts_rx_en =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 port->hwts_rx_en =3D PTP_SLAVE_MODE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 __raw_writel(0, ®s->channel[ch].Ch_Contr=
ol);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 port->hwts_rx_en =3D PTP_MASTER_MODE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 __raw_writel(MASTER_MODE, ®s->channel[ch=
].Ch_Control);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ERANGE;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Clear out any old time stamps. */
> + =A0 =A0 =A0 __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0®s->channel[ch].Ch_Event);
> +
> + =A0 =A0 =A0 return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EF=
AULT : 0;
> +}
>
> =A0static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int locati=
on,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int write, u16 cmd)
> @@ -573,6 +750,7 @@ static int eth_poll(struct napi_struct *napi, int bud=
get)
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0debug_pkt(dev, "eth_poll", skb->data, skb-=
>len);
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 do_rx_timestamp(port, skb);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0skb->protocol =3D eth_type_trans(skb, dev)=
;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dev->stats.rx_packets++;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dev->stats.rx_bytes +=3D skb->len;
> @@ -652,6 +830,7 @@ static int eth_xmit(struct sk_buff *skb, struct net_d=
evice *dev)
> =A0 =A0 =A0 =A0void *mem;
> =A0 =A0 =A0 =A0u32 phys;
> =A0 =A0 =A0 =A0struct desc *desc;
> + =A0 =A0 =A0 union skb_shared_tx *shtx;
>
> =A0#if DEBUG_TX
> =A0 =A0 =A0 =A0printk(KERN_DEBUG "%s: eth_xmit\n", dev->name);
> @@ -665,6 +844,10 @@ static int eth_xmit(struct sk_buff *skb, struct net_=
device *dev)
>
> =A0 =A0 =A0 =A0debug_pkt(dev, "eth_xmit", skb->data, skb->len);
>
> + =A0 =A0 =A0 shtx =3D skb_tx(skb);
> + =A0 =A0 =A0 if (unlikely(shtx->hardware && port->hwts_tx_en))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 shtx->in_progress =3D 1;
> +
> =A0 =A0 =A0 =A0len =3D skb->len;
> =A0#ifdef __ARMEB__
> =A0 =A0 =A0 =A0offset =3D 0; /* no need to keep alignment */
> @@ -728,6 +911,9 @@ static int eth_xmit(struct sk_buff *skb, struct net_d=
evice *dev)
> =A0#if DEBUG_TX
> =A0 =A0 =A0 =A0printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name);
> =A0#endif
> +
> + =A0 =A0 =A0 do_tx_timestamp(port, skb);
> +
> =A0 =A0 =A0 =A0return NETDEV_TX_OK;
> =A0}
>
> @@ -783,6 +969,9 @@ static int eth_ioctl(struct net_device *dev, struct i=
freq *req, int cmd)
> =A0 =A0 =A0 =A0if (!netif_running(dev))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
>
> + =A0 =A0 =A0 if (cpu_is_ixp46x() && cmd =3D=3D SIOCSHWTSTAMP)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return hwtstamp_ioctl(dev, req, cmd);
> +
> =A0 =A0 =A0 =A0return phy_mii_ioctl(port->phydev, req, cmd);
> =A0}
>
> @@ -1171,6 +1360,11 @@ static int __devinit eth_init_one(struct platform_=
device *pdev)
> =A0 =A0 =A0 =A0char phy_id[MII_BUS_ID_SIZE + 3];
> =A0 =A0 =A0 =A0int err;
>
> + =A0 =A0 =A0 if (sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("ixp4xx_eth: bad ptp filter\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> =A0 =A0 =A0 =A0if (!(dev =3D alloc_etherdev(sizeof(struct port))))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -ENOMEM;
>
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 3b7bd73..9fb35f6 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -48,4 +48,17 @@ config PTP_1588_CLOCK_GIANFAR
> =A0 =A0 =A0 =A0 =A0To compile this driver as a module, choose M here: the=
module
> =A0 =A0 =A0 =A0 =A0will be called gianfar_ptp.
>
> +config PTP_1588_CLOCK_IXP46X
> + =A0 =A0 =A0 tristate "Intel IXP46x as PTP clock"
> + =A0 =A0 =A0 depends on PTP_1588_CLOCK
> + =A0 =A0 =A0 depends on IXP4XX_ETH
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 This driver adds support for using the IXP46X as a PTP
> + =A0 =A0 =A0 =A0 clock. This clock is only useful if your PTP programs a=
re
> + =A0 =A0 =A0 =A0 getting hardware time stamps on the PTP Ethernet packet=
s
> + =A0 =A0 =A0 =A0 using the SO_TIMESTAMPING API.
> +
> + =A0 =A0 =A0 =A0 To compile this driver as a module, choose M here: the =
module
> + =A0 =A0 =A0 =A0 will be called ptp_ixp46x.
> +
> =A0endmenu
> 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 @@
>
> =A0obj-$(CONFIG_PTP_1588_CLOCK) =A0 =A0 =A0 =A0 =A0 +=3D ptp_clock.o
> =A0obj-$(CONFIG_PTP_1588_CLOCK_LINUX) =A0 =A0 +=3D ptp_linux.o
> +obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) =A0 =A0+=3D ptp_ixp46x.o
> diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
> new file mode 100644
> index 0000000..22c5bc3
> --- /dev/null
> +++ b/drivers/ptp/ptp_ixp46x.c
> @@ -0,0 +1,231 @@
> +/*
> + * PTP 1588 clock using the IXP46X
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include <linux/ptp_clock_kernel.h>
> +#include <mach/ixp46x_ts.h>
> +
> +DEFINE_SPINLOCK(register_lock);
> +
> +/*
> + * Register access functions
> + */
> +
> +static inline u32 ixp_read(volatile unsigned __iomem *addr)
> +{
> + =A0 =A0 =A0 u32 val;
> + =A0 =A0 =A0 val =3D __raw_readl(addr);
> + =A0 =A0 =A0 return val;
> +}
return __raw_readl(addr) perhaps?
> +
> +static inline void ixp_write(volatile unsigned __iomem *addr, u32 val)
> +{
> + =A0 =A0 =A0 __raw_writel(val, addr);
> +}
> +
> +static u64 sys_time_read(struct ixp46x_ts_regs *regs)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 lo, hi;
> +
> + =A0 =A0 =A0 lo =3D ixp_read(®s->SysTimeLo);
> + =A0 =A0 =A0 hi =3D ixp_read(®s->SysTimeHi);
> +
> + =A0 =A0 =A0 ns =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 ns |=3D lo;
> + =A0 =A0 =A0 ns <<=3D TICKS_NS_SHIFT;
> +
> + =A0 =A0 =A0 return ns;
> +}
> +
> +static void sys_time_write(struct ixp46x_ts_regs *regs, u64 ns)
Should use the ptp_ixp_ prefix on these functions too.
> +{
> + =A0 =A0 =A0 u32 hi, lo;
> +
> + =A0 =A0 =A0 ns >>=3D TICKS_NS_SHIFT;
> + =A0 =A0 =A0 hi =3D ns >> 32;
> + =A0 =A0 =A0 lo =3D ns & 0xffffffff;
> +
> + =A0 =A0 =A0 ixp_write(®s->SysTimeLo, lo);
> + =A0 =A0 =A0 ixp_write(®s->SysTimeHi, hi);
> +}
> +
> +/*
> + * PTP clock operations
> + */
> +
> +static int ptp_ixp_adjfreq(void *priv, s32 ppb)
> +{
> + =A0 =A0 =A0 u64 adj;
> + =A0 =A0 =A0 u32 diff, addend;
> + =A0 =A0 =A0 int neg_adj =3D 0;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs =3D priv;
> +
> + =A0 =A0 =A0 if (!ppb)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (ppb < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 neg_adj =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ppb =3D -ppb;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 addend =3D DEFAULT_ADDEND;
> + =A0 =A0 =A0 adj =3D addend;
> + =A0 =A0 =A0 adj *=3D ppb;
> + =A0 =A0 =A0 diff =3D div_u64(adj, 1000000000ULL);
> +
> + =A0 =A0 =A0 addend =3D neg_adj ? addend - diff : addend + diff;
> +
> + =A0 =A0 =A0 ixp_write(®s->Addend, addend);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_ixp_adjtime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 s64 delta, now;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs =3D priv;
> +
> + =A0 =A0 =A0 delta =3D 1000000000LL * ts->tv_sec;
> + =A0 =A0 =A0 delta +=3D ts->tv_nsec;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 now =3D sys_time_read(regs);
> + =A0 =A0 =A0 now +=3D delta;
> + =A0 =A0 =A0 sys_time_write(regs, now);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_ixp_gettime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 remainder;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs =3D priv;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 ns =3D sys_time_read(regs);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 ts->tv_sec =3D div_u64_rem(ns, 1000000000, &remainder);
> + =A0 =A0 =A0 ts->tv_nsec =3D remainder;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_ixp_settime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs =3D priv;
> +
> + =A0 =A0 =A0 ns =3D ts->tv_sec * 1000000000ULL;
> + =A0 =A0 =A0 ns +=3D ts->tv_nsec;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 sys_time_write(regs, ns);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_ixp_gettimer(void *priv, int index, struct itimerspec *ts=
)
> +{
> + =A0 =A0 =A0 /* We do not offer any ancillary features at all. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static int ptp_ixp_settimer(void *p, int i, int abs, struct itimerspec *=
ts)
> +{
> + =A0 =A0 =A0 /* We do not offer any ancillary features at all. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static int ptp_ixp_enable(void *priv, struct ptp_clock_request *rq, int =
on)
> +{
> + =A0 =A0 =A0 /* We do not offer any ancillary features at all. */
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static struct ptp_clock_info ptp_ixp_caps =3D {
> + =A0 =A0 =A0 .owner =A0 =A0 =A0 =A0 =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "IXP46X timer",
> + =A0 =A0 =A0 .max_adj =A0 =A0 =A0 =A0=3D 512000,
> + =A0 =A0 =A0 .n_alarm =A0 =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .n_ext_ts =A0 =A0 =A0 =3D 0,
> + =A0 =A0 =A0 .n_per_out =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .pps =A0 =A0 =A0 =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .priv =A0 =A0 =A0 =A0 =A0 =3D NULL,
If the value is '0' or NULL, just leave them out of the structure initializ=
er.
> + =A0 =A0 =A0 .adjfreq =A0 =A0 =A0 =A0=3D ptp_ixp_adjfreq,
> + =A0 =A0 =A0 .adjtime =A0 =A0 =A0 =A0=3D ptp_ixp_adjtime,
> + =A0 =A0 =A0 .gettime =A0 =A0 =A0 =A0=3D ptp_ixp_gettime,
> + =A0 =A0 =A0 .settime =A0 =A0 =A0 =A0=3D ptp_ixp_settime,
> + =A0 =A0 =A0 .gettimer =A0 =A0 =A0 =3D ptp_ixp_gettimer,
> + =A0 =A0 =A0 .settimer =A0 =A0 =A0 =3D ptp_ixp_settimer,
> + =A0 =A0 =A0 .enable =A0 =A0 =A0 =A0 =3D ptp_ixp_enable,
> +};
> +
> +/* module operations */
> +
> +static struct {
> + =A0 =A0 =A0 struct ixp46x_ts_regs *regs;
> + =A0 =A0 =A0 struct ptp_clock *ptp_clock;
> +} ixp_clock;
> +
> +static void __exit ptp_ixp_exit(void)
> +{
> + =A0 =A0 =A0 ptp_clock_unregister(ixp_clock.ptp_clock);
> +}
> +
> +static int __init ptp_ixp_init(void)
> +{
> + =A0 =A0 =A0 ixp_clock.regs =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 (struct ixp46x_ts_regs __iomem *)IXP4XX_TIM=
ESYNC_BASE_VIRT;
> +
> + =A0 =A0 =A0 ptp_ixp_caps.priv =3D ixp_clock.regs;
> +
> + =A0 =A0 =A0 ixp_clock.ptp_clock =3D ptp_clock_register(&ptp_ixp_caps);
> +
> + =A0 =A0 =A0 if (IS_ERR(ixp_clock.ptp_clock))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return PTR_ERR(ixp_clock.ptp_clock);
> +
> + =A0 =A0 =A0 ixp_write(&ixp_clock.regs->Addend, DEFAULT_ADDEND);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +module_init(ptp_ixp_init);
> +module_exit(ptp_ixp_exit);
Keep module_init and module_exit with their respective function declaration=
s.
g.
^ permalink raw reply
* Re: [PATCH 05/12] phylib: Allow reading and writing a mii bus from atomic context.
From: Grant Likely @ 2010-06-15 18:29 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, devicetree-discuss, Thomas Gleixner, linuxppc-dev,
linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <20100615170806.GB10668@riccoc20.at.omicron.at>
On Tue, Jun 15, 2010 at 11:08 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> On Tue, Jun 15, 2010 at 10:43:08AM -0600, Grant Likely wrote:
>> On Tue, Jun 15, 2010 at 10:08 AM, Richard Cochran
>> <richardcochran@gmail.com> wrote:
>> > In order to support hardware time stamping from a PHY, it is necessary=
to
>> > read from the PHY while running in_interrupt(). This patch allows a mi=
i
>> > bus to operate in an atomic context. An mii_bus driver may declare its=
elf
>> > capable for this mode. Drivers which do not do this will remain with t=
he
>> > default that bus operations may sleep.
>> >
>> > Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
>>
>> Last I checked, the MDIO bus is very slow. =A0Is this really a good
>> idea? =A0How much latency does MDIO access have on the hardware you are
>> working with?
>
> Yes, MDIO access is slow, and it can vary (eg bit banging
> implementations). It clear that getting PHY timestamps is costly, but
> for applications that want PTP synchronization, one is willing to pay
> the price.
>
>> I also don't like the idea of taking a spin lock during MDIO
>> operations, and the dual locking mode in the core code.
>
> Originally, the phylib used a spinlock for this. It was replaced with
> a mutex in 35b5f6b1a82b5c586e0b24c711dc6ba944e88ef1 in order to
> accommodate mdio busses that may need to sleep. So, keeping the option
> to use a spinlock is similar to the previous implementation.
That's right, and I fully agree with that change. To me, going back
to allowing spin locks is a regression because it adds a new source of
scheduling latency. Using a mutex forces users to take into account
the slow nature of MDIO access. For existing callers, this isn't a
problem because they already are designed for this characteristic. A
new user which depends on atomic access should use a different API
which doesn't take the lock with the understanding that it is may
return a failure if it doesn't support it or if it cannot perform the
operation atomically.
That still leaves the troubling MDIO induced latency issue.
g.
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 10/12] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Grant Likely @ 2010-06-15 17:20 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, devicetree-discuss, linuxppc-dev, linux-arm-kernel,
Krzysztof Halasa
In-Reply-To: <041faf3cb6ecbedf8f3ff2104914f91d2a24fc4d.1276615626.git.richard.cochran@omicron.at>
On Tue, Jun 15, 2010 at 10:10 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> 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>
Hi Richard,
comments below...
> ---
> =A0Documentation/powerpc/dts-bindings/fsl/tsec.txt | =A0 54 +++
> =A0arch/powerpc/boot/dts/mpc8313erdb.dts =A0 =A0 =A0 =A0 =A0 | =A0 13 +
> =A0arch/powerpc/boot/dts/p2020ds.dts =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0 13=
+
> =A0arch/powerpc/boot/dts/p2020rdb.dts =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0 13=
+
> =A0drivers/net/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0| =A0 =A01 +
> =A0drivers/net/gianfar_ptp.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
| =A0518 +++++++++++++++++++++++
> =A0drivers/net/gianfar_ptp_reg.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =
=A0113 +++++
Is this header file used by anything other than gianfar_ptp.c? If
not, then roll the two files together.
> =A0drivers/ptp/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 | =A0 13 +
> =A08 files changed, 738 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/net/gianfar_ptp.c
> =A0create mode 100644 drivers/net/gianfar_ptp_reg.h
>
> diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentat=
ion/powerpc/dts-bindings/fsl/tsec.txt
> index edb7ae1..14577e9 100644
> --- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
> +++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
> @@ -74,3 +74,57 @@ Example:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0interrupt-parent =3D <&mpic>;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0phy-handle =3D <&phy0>
> =A0 =A0 =A0 =A0};
> +
> +* Gianfar PTP clock nodes
> +
> +General Properties:
> +
> + =A0- compatible =A0 Should be "fsl,etsec-ptp"
> + =A0- reg =A0 =A0 =A0 =A0 =A0Offset and length of the register set for t=
he device
> + =A0- interrupts =A0 There should be at least two interrupts. Some devic=
es
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 have as many as four PTP related interr=
upts.
> +
> +Clock Properties:
> +
> + =A0- tclk_period =A0Timer reference clock period in nanoseconds.
> + =A0- tmr_prsc =A0 =A0 Prescaler, divides the output clock.
> + =A0- tmr_add =A0 =A0 =A0Frequency compensation value.
Use dash ('-') not underscore ('_') in property names.
> + =A0- cksel =A0 =A0 =A0 =A00=3D external clock, 1=3D eTSEC system clock,=
3=3D RTC clock input.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Currently the driver only supports choi=
ce "1".
If you encode this value as a string, then it will be friendly for humans t=
oo.
> + =A0- tmr_fiper1 =A0 Fixed interval period pulse generator.
> + =A0- tmr_fiper2 =A0 Fixed interval period pulse generator.
I could use more explication here. Is this a divider value?
Computers are good at making calculations, and the driver can obtain
the clock frequency supplied to the device. It may be more useful to
specify here the desired frequency rather than the divider. Certainly
more human-friendly too.
> +
> + =A0These properties set the operational parameters for the PTP
> + =A0clock. You must choose these carefully for the clock to work right.
> + =A0Here is how to figure good values:
> +
> + =A0TimerOsc =A0 =A0 =3D system clock =A0 =A0 =A0 =A0 =A0 =A0 =A0 MHz
> + =A0tclk_period =A0=3D desired clock period =A0 =A0 =A0 nanoseconds
> + =A0NominalFreq =A0=3D 1000 / tclk_period =A0 =A0 =A0 =A0 MHz
> + =A0FreqDivRatio =3D TimerOsc / NominalFreq =A0 =A0 (must be greater tha=
t 1.0)
> + =A0tmr_add =A0 =A0 =A0=3D ceil(2^32 / FreqDivRatio)
> + =A0OutputClock =A0=3D NominalFreq / tmr_prsc =A0 =A0 MHz
> + =A0PulseWidth =A0 =3D 1 / OutputClock =A0 =A0 =A0 =A0 =A0 =A0microsecon=
ds
> + =A0FiperFreq1 =A0 =3D desired frequency in Hz
> + =A0FiperDiv1 =A0 =A0=3D 1000000 * OutputClock / FiperFreq1
> + =A0tmr_fiper1 =A0 =3D tmr_prsc * tclk_period * FiperDiv1 - tclk_period
> +
> + =A0The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
> + =A0driver expects that tmr_fiper1 will be correctly set to produce a 1
> + =A0Pulse Per Second (PPS) signal, since this will be offered to the PPS
> + =A0subsystem to synchronize the Linux clock.
> +
> +Example:
> +
> + =A0 =A0 =A0 ptp_clock@24E00 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,etsec-ptp";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x24E00 0xB0>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupts =3D <12 0x8 13 0x8>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupt-parent =3D < &ipic >;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tclk_period =3D <10>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_prsc =A0 =A0=3D <100>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_add =A0 =A0 =3D <0x999999A4>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cksel =A0 =A0 =A0 =3D <0x1>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper1 =A0=3D <0x3B9AC9F6>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper2 =A0=3D <0x00018696>;
> + =A0 =A0 =A0 };
> diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dt=
s/mpc8313erdb.dts
> index 183f2aa..0526384 100644
> --- a/arch/powerpc/boot/dts/mpc8313erdb.dts
> +++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
> @@ -208,6 +208,19 @@
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sleep =3D <&pmc 0x00300000=
>;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0};
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock@24E00 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,etsec-p=
tp";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x24E00 0xB0>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupts =3D <12 0x8 13 0=
x8>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupt-parent =3D < &ipi=
c >;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tclk_period =3D <10>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_prsc =A0 =A0=3D <100>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_add =A0 =A0 =3D <0x9999=
99A4>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 cksel =A0 =A0 =A0 =3D <0x1>=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper1 =A0=3D <0x3B9AC9=
F6>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper2 =A0=3D <0x000186=
96>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
> +
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enet0: ethernet@24000 {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#address-cells =3D <1>;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#size-cells =3D <1>;
> diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2=
020ds.dts
> index 1101914..ae5dc4d 100644
> --- a/arch/powerpc/boot/dts/p2020ds.dts
> +++ b/arch/powerpc/boot/dts/p2020ds.dts
> @@ -336,6 +336,19 @@
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0phy_type =3D "ulpi";
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0};
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock@24E00 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,etsec-p=
tp";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x24E00 0xB0>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupts =3D <68 2 69 2 7=
0 2>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupt-parent =3D < &mpi=
c >;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tclk_period =3D <5>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_prsc =3D <200>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_add =3D <0xCCCCCCCD>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 cksel =3D <1>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper1 =3D <0x3B9AC9FB>=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper2 =3D <0x0001869B>=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
> +
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enet0: ethernet@24000 {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#address-cells =3D <1>;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#size-cells =3D <1>;
> diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p=
2020rdb.dts
> index da4cb0d..133945c 100644
> --- a/arch/powerpc/boot/dts/p2020rdb.dts
> +++ b/arch/powerpc/boot/dts/p2020rdb.dts
> @@ -396,6 +396,19 @@
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0phy_type =3D "ulpi";
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0};
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock@24E00 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,etsec-p=
tp";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x24E00 0xB0>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupts =3D <68 2 69 2 7=
0 2>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupt-parent =3D < &mpi=
c >;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tclk_period =3D <5>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_prsc =3D <200>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_add =3D <0xCCCCCCCD>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 cksel =3D <1>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper1 =3D <0x3B9AC9FB>=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmr_fiper2 =3D <0x0001869B>=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
> +
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enet0: ethernet@24000 {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#address-cells =3D <1>;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#size-cells =3D <1>;
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..389c0d9 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_ATL2) +=3D atlx/
> =A0obj-$(CONFIG_ATL1E) +=3D atl1e/
> =A0obj-$(CONFIG_ATL1C) +=3D atl1c/
> =A0obj-$(CONFIG_GIANFAR) +=3D gianfar_driver.o
> +obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) +=3D gianfar_ptp.o
> =A0obj-$(CONFIG_TEHUTI) +=3D tehuti.o
> =A0obj-$(CONFIG_ENIC) +=3D enic/
> =A0obj-$(CONFIG_JME) +=3D jme.o
> diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
> new file mode 100644
> index 0000000..0991652
> --- /dev/null
> +++ b/drivers/net/gianfar_ptp.c
> @@ -0,0 +1,518 @@
> +/*
> + * PTP 1588 clock using the eTSEC
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, 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 =A0 =A0 =A0 =A0 "gianfar_ptp"
> +#define N_ALARM =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01 /* first alarm is used =
internally to reset fipers */
> +#define N_EXT_TS =A0 =A0 =A0 2
> +#define REG_SIZE =A0 =A0 =A0 sizeof(struct gianfar_ptp_registers)
> +
> +struct etsects {
> + =A0 =A0 =A0 struct gianfar_ptp_registers *regs;
> + =A0 =A0 =A0 struct ptp_clock *clock;
> + =A0 =A0 =A0 int irq;
> + =A0 =A0 =A0 u64 alarm_interval; /* for periodic alarm */
> + =A0 =A0 =A0 u64 alarm_value;
> + =A0 =A0 =A0 u32 tclk_period; =A0/* nanoseconds */
> + =A0 =A0 =A0 u32 tmr_prsc;
> + =A0 =A0 =A0 u32 tmr_add;
> + =A0 =A0 =A0 u32 cksel;
> + =A0 =A0 =A0 u32 tmr_fiper1;
> + =A0 =A0 =A0 u32 tmr_fiper2;
> +};
> +
> +/* Private globals */
> +static struct etsects the_clock;
Will there ever be multiple instances of this device?
> +DEFINE_SPINLOCK(register_lock);
> +
> +/*
> + * Register access functions
> + */
> +
> +static u64 tmr_cnt_read(struct etsects *etsects)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 lo, hi;
> +
> + =A0 =A0 =A0 lo =3D gfar_read(&etsects->regs->tmr_cnt_l);
> + =A0 =A0 =A0 hi =3D gfar_read(&etsects->regs->tmr_cnt_h);
> + =A0 =A0 =A0 ns =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 ns |=3D lo;
> + =A0 =A0 =A0 return ns;
> +}
> +
> +static void tmr_cnt_write(struct etsects *etsects, u64 ns)
> +{
> + =A0 =A0 =A0 u32 hi =3D ns >> 32;
> + =A0 =A0 =A0 u32 lo =3D ns & 0xffffffff;
> +
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_cnt_l, lo);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_cnt_h, hi);
> +}
> +
> +static void set_alarm(struct etsects *etsects)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 lo, hi;
> +
> + =A0 =A0 =A0 ns =3D tmr_cnt_read(etsects) + 1500000000ULL;
> + =A0 =A0 =A0 ns =3D div_u64(ns, 1000000000UL) * 1000000000ULL;
> + =A0 =A0 =A0 ns -=3D etsects->tclk_period;
> + =A0 =A0 =A0 hi =3D ns >> 32;
> + =A0 =A0 =A0 lo =3D ns & 0xffffffff;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_alarm1_l, lo);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_alarm1_h, hi);
> +}
> +
> +static void set_fipers(struct etsects *etsects)
> +{
> + =A0 =A0 =A0 u32 tmr_ctrl =3D gfar_read(&etsects->regs->tmr_ctrl);
> +
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_ctrl, =A0 tmr_ctrl & (~TE));
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_prsc, =A0 etsects->tmr_prsc)=
;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1)=
;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2)=
;
> + =A0 =A0 =A0 set_alarm(etsects);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_ctrl, =A0 tmr_ctrl|TE);
> +}
> +
> +/*
> + * Interrupt service routine
> + */
> +
> +static irqreturn_t isr(int irq, void *priv)
> +{
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> + =A0 =A0 =A0 struct ptp_clock_event event;
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 ack =3D 0, lo, hi, mask, val;
> +
> + =A0 =A0 =A0 val =3D gfar_read(&etsects->regs->tmr_tevent);
> +
> + =A0 =A0 =A0 if (val & ETS1) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ack |=3D ETS1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hi =3D gfar_read(&etsects->regs->tmr_etts1_=
h);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 lo =3D gfar_read(&etsects->regs->tmr_etts1_=
l);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.type =3D PTP_CLOCK_EXTTS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.index =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.timestamp =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.timestamp |=3D lo;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock_event(etsects->clock, &event);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (val & ETS2) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ack |=3D ETS2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hi =3D gfar_read(&etsects->regs->tmr_etts2_=
h);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 lo =3D gfar_read(&etsects->regs->tmr_etts2_=
l);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.type =3D PTP_CLOCK_EXTTS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.index =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.timestamp =3D ((u64) hi) << 32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.timestamp |=3D lo;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock_event(etsects->clock, &event);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (val & ALM2) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ack |=3D ALM2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (etsects->alarm_value) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.type =3D PTP_CLOCK_AL=
ARM;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.index =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 event.timestamp =3D etsects=
->alarm_value;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ptp_clock_event(etsects->cl=
ock, &event);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (etsects->alarm_interval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ns =3D etsects->alarm_value=
+ etsects->alarm_interval;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hi =3D ns >> 32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 lo =3D ns & 0xffffffff;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock(®ister_lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_alarm2_l, lo);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_alarm2_h, hi);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock(®ister_lock)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 etsects->alarm_value =3D ns=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_tevent, ALM2);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock(®ister_lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask =3D gfar_read(&etsects=
->regs->tmr_temask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask &=3D ~ALM2EN;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_temask, mask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock(®ister_lock)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 etsects->alarm_value =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 etsects->alarm_interval =3D=
0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (ack) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_tevent, ack)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_HANDLED;
> + =A0 =A0 =A0 } else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_NONE;
> +}
> +
> +/*
> + * PTP clock operations
> + */
> +
> +static int ptp_gianfar_adjfreq(void *priv, s32 ppb)
> +{
> + =A0 =A0 =A0 u64 adj;
> + =A0 =A0 =A0 u32 diff, tmr_add;
> + =A0 =A0 =A0 int neg_adj =3D 0;
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> +
> + =A0 =A0 =A0 if (!ppb)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (ppb < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 neg_adj =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ppb =3D -ppb;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 tmr_add =3D etsects->tmr_add;
> + =A0 =A0 =A0 adj =3D tmr_add;
> + =A0 =A0 =A0 adj *=3D ppb;
> + =A0 =A0 =A0 diff =3D div_u64(adj, 1000000000ULL);
> +
> + =A0 =A0 =A0 tmr_add =3D neg_adj ? tmr_add - diff : tmr_add + diff;
> +
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_add, tmr_add);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_adjtime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 s64 delta, now;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> +
> + =A0 =A0 =A0 delta =3D 1000000000LL * ts->tv_sec;
> + =A0 =A0 =A0 delta +=3D ts->tv_nsec;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 now =3D tmr_cnt_read(etsects);
> + =A0 =A0 =A0 now +=3D delta;
> + =A0 =A0 =A0 tmr_cnt_write(etsects, now);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 set_fipers(etsects);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_gettime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 remainder;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 ns =3D tmr_cnt_read(etsects);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 ts->tv_sec =3D div_u64_rem(ns, 1000000000, &remainder);
> + =A0 =A0 =A0 ts->tv_nsec =3D remainder;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_settime(void *priv, struct timespec *ts)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> +
> + =A0 =A0 =A0 ns =3D ts->tv_sec * 1000000000ULL;
> + =A0 =A0 =A0 ns +=3D ts->tv_nsec;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 tmr_cnt_write(etsects, ns);
> + =A0 =A0 =A0 set_fipers(etsects);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_gettimer(void *priv, int index, struct itimerspec=
*ts)
> +{
> + =A0 =A0 =A0 u64 now, ns;
> + =A0 =A0 =A0 u32 remainder;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> +
> + =A0 =A0 =A0 ns =3D etsects->alarm_interval;
> +
> + =A0 =A0 =A0 ts->it_interval.tv_sec =3D div_u64_rem(ns, 1000000000, &rem=
ainder);
> + =A0 =A0 =A0 ts->it_interval.tv_nsec =3D remainder;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> + =A0 =A0 =A0 now =3D tmr_cnt_read(etsects);
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 ns =3D etsects->alarm_value - now;
> +
> + =A0 =A0 =A0 ts->it_value.tv_sec =3D div_u64_rem(ns, 1000000000, &remain=
der);
> + =A0 =A0 =A0 ts->it_value.tv_nsec =3D remainder;
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_settimer(void *p, int i, int abs, struct itimersp=
ec *ts)
> +{
> + =A0 =A0 =A0 u64 ns;
> + =A0 =A0 =A0 u32 lo, hi, mask;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 struct etsects *etsects =3D p;
> +
> + =A0 =A0 =A0 ns =3D ts->it_interval.tv_sec * 1000000000ULL;
> + =A0 =A0 =A0 ns +=3D ts->it_interval.tv_nsec;
> +
> + =A0 =A0 =A0 etsects->alarm_interval =3D ns;
> +
> + =A0 =A0 =A0 ns =3D ts->it_value.tv_sec * 1000000000ULL;
> + =A0 =A0 =A0 ns +=3D ts->it_value.tv_nsec;
> +
> + =A0 =A0 =A0 if (!ns) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Cancel the timer. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 etsects->alarm_value =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 etsects->alarm_interval =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (!abs) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ns +=3D tmr_cnt_read(etsects);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flag=
s);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 etsects->alarm_value =3D ns;
> +
> + =A0 =A0 =A0 hi =3D ns >> 32;
> + =A0 =A0 =A0 lo =3D ns & 0xffffffff;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> +
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_alarm2_l, lo);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_alarm2_h, hi);
> +
> + =A0 =A0 =A0 mask =3D gfar_read(&etsects->regs->tmr_temask);
> + =A0 =A0 =A0 mask |=3D ALM2EN;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_temask, mask);
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flags);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ptp_gianfar_enable(void *priv, struct ptp_clock_request *rq, =
int on)
> +{
> + =A0 =A0 =A0 struct etsects *etsects =3D priv;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 u32 bit, mask;
> +
> + =A0 =A0 =A0 switch (rq->type) {
> + =A0 =A0 =A0 case PTP_REQUEST_EXTTS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (rq->index) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bit =3D ETS1EN;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 1:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bit =3D ETS2EN;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(®ister_lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (on) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask =3D gfar_read(&etsects=
->regs->tmr_temask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask |=3D bit;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_temask, mask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask =3D gfar_read(&etsects=
->regs->tmr_temask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mask &=3D ~bit;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gfar_write(&etsects->regs->=
tmr_temask, mask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(®ister_lock, flag=
s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return -EOPNOTSUPP;
> +}
> +
> +static struct ptp_clock_info ptp_gianfar_caps =3D {
> + =A0 =A0 =A0 .owner =A0 =A0 =A0 =A0 =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "gianfar clock",
> + =A0 =A0 =A0 .max_adj =A0 =A0 =A0 =A0=3D 512000,
> + =A0 =A0 =A0 .n_alarm =A0 =A0 =A0 =A0=3D N_ALARM,
> + =A0 =A0 =A0 .n_ext_ts =A0 =A0 =A0 =3D N_EXT_TS,
> + =A0 =A0 =A0 .n_per_out =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .pps =A0 =A0 =A0 =A0 =A0 =A0=3D 0,
> + =A0 =A0 =A0 .priv =A0 =A0 =A0 =A0 =A0 =3D &the_clock,
> + =A0 =A0 =A0 .adjfreq =A0 =A0 =A0 =A0=3D ptp_gianfar_adjfreq,
> + =A0 =A0 =A0 .adjtime =A0 =A0 =A0 =A0=3D ptp_gianfar_adjtime,
> + =A0 =A0 =A0 .gettime =A0 =A0 =A0 =A0=3D ptp_gianfar_gettime,
> + =A0 =A0 =A0 .settime =A0 =A0 =A0 =A0=3D ptp_gianfar_settime,
> + =A0 =A0 =A0 .gettimer =A0 =A0 =A0 =3D ptp_gianfar_gettimer,
> + =A0 =A0 =A0 .settimer =A0 =A0 =A0 =3D ptp_gianfar_settimer,
> + =A0 =A0 =A0 .enable =A0 =A0 =A0 =A0 =3D ptp_gianfar_enable,
> +};
> +
> +/* OF device tree */
> +
> +static int get_of_u32(struct device_node *node, char *str, u32 *val)
> +{
> + =A0 =A0 =A0 int plen;
> + =A0 =A0 =A0 const u32 *prop =3D of_get_property(node, str, &plen);
> +
> + =A0 =A0 =A0 if (!prop || plen !=3D sizeof(*prop))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -1;
> + =A0 =A0 =A0 *val =3D *prop;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int gianfar_ptp_probe(struct of_device *dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0const struct of_=
device_id *match)
> +{
> + =A0 =A0 =A0 u64 addr, size;
> + =A0 =A0 =A0 struct device_node *node =3D dev->dev.of_node;
> + =A0 =A0 =A0 struct etsects *etsects =3D &the_clock;
> + =A0 =A0 =A0 struct timespec now;
> + =A0 =A0 =A0 phys_addr_t reg_addr;
> + =A0 =A0 =A0 unsigned long reg_size;
> + =A0 =A0 =A0 u32 tmr_ctrl;
> +
> + =A0 =A0 =A0 if (get_of_u32(node, "tclk_period", &etsects->tclk_period) =
||
> + =A0 =A0 =A0 =A0 =A0 get_of_u32(node, "tmr_prsc", &etsects->tmr_prsc) ||
> + =A0 =A0 =A0 =A0 =A0 get_of_u32(node, "tmr_add", &etsects->tmr_add) ||
> + =A0 =A0 =A0 =A0 =A0 get_of_u32(node, "cksel", &etsects->cksel) ||
> + =A0 =A0 =A0 =A0 =A0 get_of_u32(node, "tmr_fiper1", &etsects->tmr_fiper1=
) ||
> + =A0 =A0 =A0 =A0 =A0 get_of_u32(node, "tmr_fiper2", &etsects->tmr_fiper2=
)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("device tree node missing required e=
lements\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 etsects->irq =3D irq_of_parse_and_map(node, 0);
> +
> + =A0 =A0 =A0 if (etsects->irq =3D=3D NO_IRQ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("irq not in device tree\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("request_irq failed\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 addr =3D of_translate_address(node, of_get_address(node, 0,=
&size, NULL));
> + =A0 =A0 =A0 reg_addr =3D addr;
> + =A0 =A0 =A0 reg_size =3D size;
> + =A0 =A0 =A0 if (reg_size < REG_SIZE) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_warning("device tree reg range %lu too s=
mall\n", reg_size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg_size =3D REG_SIZE;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 etsects->regs =3D ioremap(reg_addr, reg_size);
> + =A0 =A0 =A0 if (!etsects->regs) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("ioremap ptp registers failed\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
Consider of_iomap(), it will simplify the code a bit.
> +
> + =A0 =A0 =A0 tmr_ctrl =3D
> + =A0 =A0 =A0 =A0 (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIO=
D_SHIFT |
> + =A0 =A0 =A0 =A0 (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
> +
> + =A0 =A0 =A0 getnstimeofday(&now);
> + =A0 =A0 =A0 ptp_gianfar_settime(etsects, &now);
> +
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_ctrl, =A0 tmr_ctrl);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_add, =A0 =A0etsects->tmr_add=
);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_prsc, =A0 etsects->tmr_prsc)=
;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1)=
;
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2)=
;
> + =A0 =A0 =A0 set_alarm(etsects);
> + =A0 =A0 =A0 gfar_write(&etsects->regs->tmr_ctrl, =A0 tmr_ctrl|FS|RTPE|T=
E);
> +
> + =A0 =A0 =A0 etsects->clock =3D ptp_clock_register(&ptp_gianfar_caps);
> +
> + =A0 =A0 =A0 return IS_ERR(etsects->clock) ? PTR_ERR(etsects->clock) : 0=
;
> +}
> +
> +static int gianfar_ptp_remove(struct of_device *dev)
> +{
> + =A0 =A0 =A0 gfar_write(&the_clock.regs->tmr_temask, 0);
> + =A0 =A0 =A0 gfar_write(&the_clock.regs->tmr_ctrl, =A0 0);
> +
> + =A0 =A0 =A0 ptp_clock_unregister(the_clock.clock);
> +
> + =A0 =A0 =A0 free_irq(the_clock.irq, &the_clock);
> +
> + =A0 =A0 =A0 iounmap(the_clock.regs);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static struct of_device_id match_table[] =3D {
> + =A0 =A0 =A0 { .compatible =3D "fsl,etsec-ptp" },
> + =A0 =A0 =A0 {},
> +};
> +
> +static struct of_platform_driver gianfar_ptp_driver =3D {
> + =A0 =A0 =A0 .driver =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "gianfar_ptp"=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .of_match_table =3D match_table,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .owner =A0 =A0 =A0 =A0 =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 },
> + =A0 =A0 =A0 .probe =A0 =A0 =A0 =3D gianfar_ptp_probe,
> + =A0 =A0 =A0 .remove =A0 =A0 =A0=3D gianfar_ptp_remove,
> +};
> +
> +/* module operations */
> +
> +static void __exit ptp_gianfar_exit(void)
> +{
> + =A0 =A0 =A0 of_unregister_platform_driver(&gianfar_ptp_driver);
> +}
> +
> +static int __init ptp_gianfar_init(void)
> +{
> + =A0 =A0 =A0 return of_register_platform_driver(&gianfar_ptp_driver);
> +}
> +
> +module_init(ptp_gianfar_init);
Move ptp_gianfar_exit() definition here so it is immediately before
the module_exit() line.
> +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
> + *
> + * =A0This program is free software; you can redistribute it and/or modi=
fy
> + * =A0it under the terms of the GNU General Public License as published =
by
> + * =A0the Free Software Foundation; either version 2 of the License, or
> + * =A0(at your option) any later version.
> + *
> + * =A0This program is distributed in the hope that it will be useful,
> + * =A0but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * =A0MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
> + * =A0GNU General Public License for more details.
> + *
> + * =A0You should have received a copy of the GNU General Public License
> + * =A0along with this program; if not, write to the Free Software
> + * =A0Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#ifndef _GIANFAR_PTP_REG_H_
> +#define _GIANFAR_PTP_REG_H_
> +
> +struct gianfar_ptp_registers {
> + =A0 =A0 =A0 u32 tmr_ctrl; =A0 =A0 /* Timer control register */
> + =A0 =A0 =A0 u32 tmr_tevent; =A0 /* Timestamp event register */
> + =A0 =A0 =A0 u32 tmr_temask; =A0 /* Timer event mask register */
> + =A0 =A0 =A0 u32 tmr_pevent; =A0 /* Timestamp event register */
> + =A0 =A0 =A0 u32 tmr_pemask; =A0 /* Timer event mask register */
> + =A0 =A0 =A0 u32 tmr_stat; =A0 =A0 /* Timestamp status register */
> + =A0 =A0 =A0 u32 tmr_cnt_h; =A0 =A0/* Timer counter high register */
> + =A0 =A0 =A0 u32 tmr_cnt_l; =A0 =A0/* Timer counter low register */
> + =A0 =A0 =A0 u32 tmr_add; =A0 =A0 =A0/* Timer drift compensation addend =
register */
> + =A0 =A0 =A0 u32 tmr_acc; =A0 =A0 =A0/* Timer accumulator register */
> + =A0 =A0 =A0 u32 tmr_prsc; =A0 =A0 /* Timer prescale */
> + =A0 =A0 =A0 u8 =A0res1[4];
> + =A0 =A0 =A0 u32 tmroff_h; =A0 =A0 /* Timer offset high */
> + =A0 =A0 =A0 u32 tmroff_l; =A0 =A0 /* Timer offset low */
> + =A0 =A0 =A0 u8 =A0res2[8];
> + =A0 =A0 =A0 u32 tmr_alarm1_h; /* Timer alarm 1 high register */
> + =A0 =A0 =A0 u32 tmr_alarm1_l; /* Timer alarm 1 high register */
> + =A0 =A0 =A0 u32 tmr_alarm2_h; /* Timer alarm 2 high register */
> + =A0 =A0 =A0 u32 tmr_alarm2_l; /* Timer alarm 2 high register */
> + =A0 =A0 =A0 u8 =A0res3[48];
> + =A0 =A0 =A0 u32 tmr_fiper1; =A0 /* Timer fixed period interval */
> + =A0 =A0 =A0 u32 tmr_fiper2; =A0 /* Timer fixed period interval */
> + =A0 =A0 =A0 u32 tmr_fiper3; =A0 /* Timer fixed period interval */
> + =A0 =A0 =A0 u8 =A0res4[20];
> + =A0 =A0 =A0 u32 tmr_etts1_h; =A0/* Timestamp of general purpose externa=
l trigger */
> + =A0 =A0 =A0 u32 tmr_etts1_l; =A0/* Timestamp of general purpose externa=
l trigger */
> + =A0 =A0 =A0 u32 tmr_etts2_h; =A0/* Timestamp of general purpose externa=
l trigger */
> + =A0 =A0 =A0 u32 tmr_etts2_l; =A0/* Timestamp of general purpose externa=
l trigger */
> +};
> +
> +/* Bit definitions for the TMR_CTRL register */
> +#define ALM1P =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<31) /* Alarm1 output p=
olarity */
> +#define ALM2P =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<30) /* Alarm2 output p=
olarity */
> +#define FS =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<28) /* FIPER start=
indication */
> +#define PP1L =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<27) /* Fiper1 pulse =
loopback mode enabled. */
> +#define PP2L =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<26) /* Fiper2 pulse =
loopback mode enabled. */
> +#define TCLK_PERIOD_SHIFT =A0 =A0 (16) /* 1588 timer reference clock per=
iod. */
> +#define TCLK_PERIOD_MASK =A0 =A0 =A0(0x3ff)
> +#define RTPE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<15) /* Record Tx Tim=
estamp to PAL Enable. */
> +#define FRD =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<14) /* FIPER Realign=
ment Disable */
> +#define ESFDP =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<11) /* External Tx/Rx =
SFD Polarity. */
> +#define ESFDE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<10) /* External Tx/Rx =
SFD Enable. */
> +#define ETEP2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<9) /* External trigger=
2 edge polarity */
> +#define ETEP1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<8) /* External trigger=
1 edge polarity */
> +#define COPH =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<7) /* Generated cloc=
k (TSEC_1588_GCLK) output phase. */
> +#define CIPH =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<6) /* External oscil=
lator input clock phase. */
> +#define TMSR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<5) /* Timer soft res=
et. When enabled, it resets all the timer registers and state machines. */
> +#define BYP =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<3) /* Bypass drift c=
ompensated clock */
> +#define TE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<2) /* 1588 timer e=
nable. If not enabled, all the timer registers and state machines are disab=
led. */
> +#define CKSEL_SHIFT =A0 =A0 =A0 =A0 =A0 (0) /* 1588 Timer reference cloc=
k source select. */
> +#define CKSEL_MASK =A0 =A0 =A0 =A0 =A0 =A0(0x3)
> +
> +/* Bit definitions for the TMR_TEVENT register */
> +#define ETS2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<25) /* External trig=
ger 2 timestamp sampled */
> +#define ETS1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<24) /* External trig=
ger 1 timestamp sampled */
> +#define ALM2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<17) /* Current time =
equaled alarm time register 2 */
> +#define ALM1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<16) /* Current time =
equaled alarm time register 1 */
> +#define PP1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<7) /* Indicates that=
a periodic pulse has been generated based on FIPER1 register */
> +#define PP2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<6) /* Indicates that=
a periodic pulse has been generated based on FIPER2 register */
> +#define PP3 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<5) /* Indicates that=
a periodic pulse has been generated based on FIPER3 register */
> +
> +/* Bit definitions for the TMR_TEMASK register */
> +#define ETS2EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<25) /* External trigge=
r 2 timestamp sample event enable */
> +#define ETS1EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<24) /* External trigge=
r 1 timestamp sample event enable */
> +#define ALM2EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<17) /* Timer ALM2 even=
t enable */
> +#define ALM1EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<16) /* Timer ALM1 even=
t enable */
> +#define PP1EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<7) /* Periodic pulse e=
vent 1 enable */
> +#define PP2EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<6) /* Periodic pulse e=
vent 2 enable */
> +
> +/* Bit definitions for the TMR_PEVENT register */
> +#define TXP2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<9) /* Indicates that=
a PTP frame has been transmitted and its timestamp is stored in TXTS2 regi=
ster */
> +#define TXP1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<8) /* Indicates that=
a PTP frame has been transmitted and its timestamp is stored in TXTS1 regi=
ster */
> +#define RXP =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<0) /* Indicates that=
a PTP frame has been received */
> +
> +/* Bit definitions for the TMR_PEMASK register */
> +#define TXP2EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<9) /* Transmit PTP pac=
ket event 2 enable */
> +#define TXP1EN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1<<8) /* Transmit PTP pac=
ket event 1 enable */
> +#define RXPEN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1<<0) /* Receive PTP pack=
et event enable */
> +
> +/* Bit definitions for the TMR_STAT register */
> +#define STAT_VEC_SHIFT =A0 =A0 =A0 =A0(0) /* Timer general purpose statu=
s vector */
> +#define STAT_VEC_MASK =A0 =A0 =A0 =A0 (0x3f)
> +
> +/* Bit definitions for the TMR_PRSC register */
> +#define PRSC_OCK_SHIFT =A0 =A0 =A0 =A0(0) /* Output clock division/presc=
ale factor. */
> +#define PRSC_OCK_MASK =A0 =A0 =A0 =A0 (0xffff)
> +
> +#endif
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 9390d44..3b7bd73 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -35,4 +35,17 @@ config PTP_1588_CLOCK_LINUX
> =A0 =A0 =A0 =A0 =A0To compile this driver as a module, choose M here: the=
module
> =A0 =A0 =A0 =A0 =A0will be called ptp_linux.
>
> +config PTP_1588_CLOCK_GIANFAR
> + =A0 =A0 =A0 tristate "Freescale eTSEC as PTP clock"
> + =A0 =A0 =A0 depends on PTP_1588_CLOCK
> + =A0 =A0 =A0 depends on GIANFAR
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 This driver adds support for using the eTSEC as a PTP
> + =A0 =A0 =A0 =A0 clock. This clock is only useful if your PTP programs a=
re
> + =A0 =A0 =A0 =A0 getting hardware time stamps on the PTP Ethernet packet=
s
> + =A0 =A0 =A0 =A0 using the SO_TIMESTAMPING API.
> +
> + =A0 =A0 =A0 =A0 To compile this driver as a module, choose M here: the =
module
> + =A0 =A0 =A0 =A0 will be called gianfar_ptp.
> +
> =A0endmenu
> --
> 1.6.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox