* [PATCH 6/10] arch/powerpc/platforms/pseries: Use kstrdup
From: Julia Lawall @ 2010-05-14 19:30 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, linuxppc-dev,
linux-kernel, kernel-janitors
From: Julia Lawall <julia@diku.dk>
Use kstrdup when the goal of an allocation is copy a string into the
allocated region.
The semantic patch that makes this change is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@@
expression from,to;
expression flag,E1,E2;
statement S;
@@
- to = kmalloc(strlen(from) + 1,flag);
+ to = kstrdup(from, flag);
... when != \(from = E1 \| to = E1 \)
if (to==NULL || ...) S
... when != \(from = E2 \| to = E2 \)
- strcpy(to, from);
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
arch/powerpc/platforms/pseries/reconfig.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff -u -p a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -118,12 +118,10 @@ static int pSeries_reconfig_add_node(con
if (!np)
goto out_err;
- np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
+ np->full_name = kstrdup(path, GFP_KERNEL);
if (!np->full_name)
goto out_err;
- strcpy(np->full_name, path);
-
np->properties = proplist;
of_node_set_flag(np, OF_DYNAMIC);
kref_init(&np->kref);
^ permalink raw reply
* Re: [PATCH] powerpc: fix ioremap_flags() with book3e pte definition
From: Kumar Gala @ 2010-05-14 17:57 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: galak, linuxppc-dev
In-Reply-To: <1265602870.8287.232.camel@pasglop>
On Feb 7, 2010, at 10:21 PM, Benjamin Herrenschmidt wrote:
> On Fri, 2010-02-05 at 00:53 +0800, Li Yang wrote:
>> We can't just clear the user read permission in book3e pte, because
>> that will also clear supervisor read permission. This surely isn't
>> desired. Fix the problem by adding the supervisor read back.
>>=20
>> Signed-off-by: Li Yang <leoli@freescale.com>
>> ---
>> arch/powerpc/mm/pgtable_32.c | 5 +++++
>> 1 files changed, 5 insertions(+), 0 deletions(-)
>>=20
>> diff --git a/arch/powerpc/mm/pgtable_32.c =
b/arch/powerpc/mm/pgtable_32.c
>> index cb96cb2..aff7c04 100644
>> --- a/arch/powerpc/mm/pgtable_32.c
>> +++ b/arch/powerpc/mm/pgtable_32.c
>> @@ -144,6 +144,11 @@ ioremap_flags(phys_addr_t addr, unsigned long =
size, unsigned long flags)
>> /* we don't want to let _PAGE_USER and _PAGE_EXEC leak out */
>> flags &=3D ~(_PAGE_USER | _PAGE_EXEC);
>>=20
>> +#if defined(CONFIG_FSL_BOOKE) && defined(CONFIG_PTE_64BIT)
>> + /* supervisor read permission has just been cleared, add back */
>> + flags |=3D _PAGE_BAP_SR;
>> +#endif
>> +
>=20
> So this is a bit fishy indeed. pgtable_64.c seems to have the same
> problem in fact.
>=20
> It boils down to the "hack" I have in the new PTE format which =
consists
> of having _SR be part of _PAGE_USER. I wonder if I should change that
> instead. Kumar, what do you reckon ?
>=20
> _Maybe_ an option is to have _PAGE_NO_USER that defaults to be
> ~_PAGE_USER in pte-common.h and can be set in pte-book3e.h to
> only strip out UR and UW ?
We never closed on this. I think lets add _PAGE_NO_USER
- k=
^ permalink raw reply
* Re: [PATCH v3 3/3] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Scott Wood @ 2010-05-14 17:46 UTC (permalink / raw)
To: Richard Cochran; +Cc: netdev, devicetree-discuss, linuxppc-dev
In-Reply-To: <ee6c3edca3ee6aa86565e59da999375f79c9de1b.1273855017.git.richard.cochran@omicron.at>
On 05/14/2010 11:46 AM, Richard Cochran wrote:
> diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
> index edb7ae1..b09ba66 100644
> --- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
> +++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
> @@ -74,3 +74,59 @@ Example:
> interrupt-parent =<&mpic>;
> phy-handle =<&phy0>
> };
> +
> +* Gianfar PTP clock nodes
> +
> +General Properties:
> +
> + - device_type Should be "ptp_clock"
Device_type is deprecated in most contexts for flat device trees.
> + - model Model of the device. Must be "eTSEC"
Model, while abused by the current gianfar binding code, is not supposed
to be something that is ordinarily used to bind on. It is supposed to
be a freeform field for indicating the specific model of hardware,
mainly for human consumption or as a last resort for working around
problems.
Get rid of both device_type and model, and specify a compatible string
instead (e.g. "fsl,etsec-ptp"). Or perhaps this should just be some
additional properties on the existing gianfar nodes, rather than
presenting it as a separate device? How do you associate a given ptp
block with the corresponding gianfar node? If there are differences in
ptp implementation between different versions of etsec, can the ptp
driver see the etsec version register?
> + - reg Offset and length of the register set for the device
> + - interrupts There should be at least two and as many as four
> + PTP related interrupts
> +
> +Clock Properties:
> +
> + - tclk_period Timer reference clock period in nanoseconds.
> + - tmr_prsc Prescaler, divides the output clock.
> + - tmr_add Frequency compensation value.
> + - cksel 0= external clock, 1= eTSEC system clock, 3= RTC clock input.
> + Currently the driver only supports choice "1".
> + - tmr_fiper1 Fixed interval period pulse generator.
> + - tmr_fiper2 Fixed interval period pulse generator.
Dashes are more typical in OF names than underscores, and it's generally
better to be a little more verbose -- these aren't local loop iterators.
They should probably have an "fsl,ptp-" prefix as well.
> + These properties set the operational parameters for the PTP
> + clock. You must choose these carefully for the clock to work right.
Do these values describe the way the hardware is, or how it's been
configured by firmware, or a set of values that are clearly optimal for
this particular board? If it's just configuration for the Linux driver,
that could reasonably differ based on what a given user or OS will want,
the device tree probably isn't the right place for it.
> diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
> index 1101914..f72353a 100644
> --- a/arch/powerpc/boot/dts/p2020ds.dts
> +++ b/arch/powerpc/boot/dts/p2020ds.dts
> @@ -336,6 +336,20 @@
> phy_type = "ulpi";
> };
>
> + ptp_clock@24E00 {
> + device_type = "ptp_clock";
> + model = "eTSEC";
> + reg = <0x24E00 0xB0>;
> + interrupts = <68 2 69 2 70 2>;
> + interrupt-parent = < &mpic >;
> + tclk_period = <5>;
> + tmr_prsc = <200>;
> + tmr_add = <0xCCCCCCCD>;
> + cksel = <1>;
> + tmr_fiper1 = <0x3B9AC9FB>;
> + tmr_fiper2 = <0x0001869B>;
> + };
> +
This one has 3 interrupts? The driver supports only two.
> +/* Private globals */
> +static struct ptp_clock *gianfar_clock;
Do you not support more than one of these?
> +static struct etsects the_clock;
"The" clock? As oppsed to the "other" clock one line above? :-)
> +static irqreturn_t isr(int irq, void *priv)
> +{
> + struct etsects *etsects = priv;
> + struct ptp_clock_event event;
> + u64 ns;
> + u32 ack=0, lo, hi, mask, val;
> +
> + val = gfar_read(&etsects->regs->tmr_tevent);
> +
> + if (val& ETS1) {
> + ack |= ETS1;
> + hi = gfar_read(&etsects->regs->tmr_etts1_h);
> + lo = gfar_read(&etsects->regs->tmr_etts1_l);
> + event.type = PTP_CLOCK_EXTTS;
> + event.index = 0;
> + event.timestamp = ((u64) hi)<< 32;
> + event.timestamp |= lo;
> + ptp_clock_event(gianfar_clock,&event);
> + }
> +
> + if (val& ETS2) {
> + ack |= ETS2;
> + hi = gfar_read(&etsects->regs->tmr_etts2_h);
> + lo = gfar_read(&etsects->regs->tmr_etts2_l);
> + event.type = PTP_CLOCK_EXTTS;
> + event.index = 1;
> + event.timestamp = ((u64) hi)<< 32;
> + event.timestamp |= lo;
> + ptp_clock_event(gianfar_clock,&event);
> + }
> +
> + if (val& ALM2) {
> + ack |= ALM2;
> + if (etsects->alarm_value) {
> + event.type = PTP_CLOCK_ALARM;
> + event.index = 0;
> + event.timestamp = etsects->alarm_value;
> + ptp_clock_event(gianfar_clock,&event);
> + }
> + if (etsects->alarm_interval) {
> + ns = etsects->alarm_value + etsects->alarm_interval;
> + hi = ns>> 32;
> + lo = ns& 0xffffffff;
> + spin_lock(®ister_lock);
> + gfar_write(&etsects->regs->tmr_alarm2_l, lo);
> + gfar_write(&etsects->regs->tmr_alarm2_h, hi);
> + spin_unlock(®ister_lock);
> + etsects->alarm_value = ns;
> + } else {
> + gfar_write(&etsects->regs->tmr_tevent, ALM2);
> + spin_lock(®ister_lock);
> + mask = gfar_read(&etsects->regs->tmr_temask);
> + mask&= ~ALM2EN;
> + gfar_write(&etsects->regs->tmr_temask, mask);
> + spin_unlock(®ister_lock);
> + etsects->alarm_value = 0;
> + etsects->alarm_interval = 0;
> + }
> + }
> +
> + gfar_write(&etsects->regs->tmr_tevent, ack);
> +
> + return IRQ_HANDLED;
Should only return IRQ_HANDLED if you found an event.
> + if (get_of_u32(node, "tclk_period",&etsects->tclk_period) ||
> + get_of_u32(node, "tmr_prsc",&etsects->tmr_prsc) ||
> + get_of_u32(node, "tmr_add",&etsects->tmr_add) ||
> + get_of_u32(node, "cksel",&etsects->cksel) ||
> + get_of_u32(node, "tmr_fiper1",&etsects->tmr_fiper1) ||
> + get_of_u32(node, "tmr_fiper2",&etsects->tmr_fiper2))
> + return -ENODEV;
Might want to print an error so the user knows what's missing.
> + for (i = 0; i< N_IRQS; i++) {
> +
> + etsects->irq[i] = irq_of_parse_and_map(node, i);
> +
> + if (etsects->irq[i] == NO_IRQ) {
> + pr_err("irq[%d] not in device tree", i);
> + return -ENODEV;
> + }
> +
> + if (request_irq(etsects->irq[i], isr, 0, DRIVER, etsects)) {
> + pr_err("request_irq failed irq %d", etsects->irq[i]);
> + return -ENODEV;
> + }
You've got two IRQs, with the same handler, and the same dev_id? From
the manual it looks like there's one PTP interrupt per eTSEC (which
would explain 3 interrupts on p2020).
> +static struct of_device_id match_table[] = {
> + { .type = "ptp_clock" },
> + {},
> +};
This driver controls every possible PTP implementation?
-Scott
^ permalink raw reply
* Re: [PATCH]460EX on-chip SATA driver <Kernel 2.6.33> < resubmission >
From: Sergei Shtylyov @ 2010-05-14 17:44 UTC (permalink / raw)
To: Rupjyoti Sarmah; +Cc: linux-ide, sr, jgarzik, linux-kernel, linuxppc-dev
In-Reply-To: <201005051757.o45HvPmY005954@amcc.com>
Hello.
Rupjyoti Sarmah wrote:
> This patch enables the on-chip DWC SATA controller of the AppliedMicro processor 460EX.
>
> Signed-off-by: Rupjyoti Sarmah <rsarmah@appliedmicro.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld@appliedmicro.com>
> Signed-off-by: Prodyut Hazarika <phazarika@appliedmicro.com>
> ---
> arch/powerpc/boot/dts/canyonlands.dts | 8 +
> drivers/ata/Kconfig | 9 +
> drivers/ata/Makefile | 1 +
> drivers/ata/sata_dwc.c | 1827 +++++++++++++++++++++++++++++++++
> 4 files changed, 1845 insertions(+), 0 deletions(-)
> create mode 100644 drivers/ata/sata_dwc.c
>
> diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
> index cd56bb5..d3b2c99 100644
> --- a/arch/powerpc/boot/dts/canyonlands.dts
> +++ b/arch/powerpc/boot/dts/canyonlands.dts
> @@ -163,6 +163,14 @@
> interrupts = <0x1e 4>;
> };
>
> + SATA0: sata@bffd1000 {
> + compatible = "amcc,sata-460ex";
> + reg = <4 0xbffd1000 0x800 4 0xbffd0800 0x400>;
> + interrupt-parent = <&UIC3>;
> + interrupts = <0x0 0x4 /* SATA */
> + 0x5 0x4>; /* AHBDMA */
> + };
> +
> POB0: opb {
> compatible = "ibm,opb-460ex", "ibm,opb";
> #address-cells = <1>;
>
Please put the device tree update in a separate patch to go thru the
PowerPC tree.
MBR, Sergei
^ permalink raw reply
* [PATCH v3 3/3] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Richard Cochran @ 2010-05-14 16:46 UTC (permalink / raw)
To: netdev; +Cc: devicetree-discuss, linuxppc-dev
In-Reply-To: <cover.1273855016.git.richard.cochran@omicron.at>
The eTSEC includes a PTP clock with quite a few features. This patch adds
support for the basic clock adjustment functions, plus two external time
stamps and one alarm.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
Documentation/powerpc/dts-bindings/fsl/tsec.txt | 56 +++
arch/powerpc/boot/dts/mpc8313erdb.dts | 14 +
arch/powerpc/boot/dts/p2020ds.dts | 14 +
arch/powerpc/boot/dts/p2020rdb.dts | 14 +
drivers/net/Makefile | 1 +
drivers/net/gianfar_ptp.c | 521 +++++++++++++++++++++++
drivers/net/gianfar_ptp_reg.h | 113 +++++
drivers/ptp/Kconfig | 13 +
8 files changed, 746 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/gianfar_ptp.c
create mode 100644 drivers/net/gianfar_ptp_reg.h
diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
index edb7ae1..b09ba66 100644
--- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
@@ -74,3 +74,59 @@ Example:
interrupt-parent = <&mpic>;
phy-handle = <&phy0>
};
+
+* Gianfar PTP clock nodes
+
+General Properties:
+
+ - device_type Should be "ptp_clock"
+ - model Model of the device. Must be "eTSEC"
+ - reg Offset and length of the register set for the device
+ - interrupts There should be at least two and as many as four
+ PTP related interrupts
+
+Clock Properties:
+
+ - tclk_period Timer reference clock period in nanoseconds.
+ - tmr_prsc Prescaler, divides the output clock.
+ - tmr_add Frequency compensation value.
+ - cksel 0= external clock, 1= eTSEC system clock, 3= RTC clock input.
+ Currently the driver only supports choice "1".
+ - tmr_fiper1 Fixed interval period pulse generator.
+ - tmr_fiper2 Fixed interval period pulse generator.
+
+ These properties set the operational parameters for the PTP
+ clock. You must choose these carefully for the clock to work right.
+ Here is how to figure good values:
+
+ TimerOsc = system clock MHz
+ tclk_period = desired clock period nanoseconds
+ NominalFreq = 1000 / tclk_period MHz
+ FreqDivRatio = TimerOsc / NominalFreq (must be greater that 1.0)
+ tmr_add = ceil(2^32 / FreqDivRatio)
+ OutputClock = NominalFreq / tmr_prsc MHz
+ PulseWidth = 1 / OutputClock microseconds
+ FiperFreq1 = desired frequency in Hz
+ FiperDiv1 = 1000000 * OutputClock / FiperFreq1
+ tmr_fiper1 = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
+
+ The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
+ driver expects that tmr_fiper1 will be correctly set to produce a 1
+ Pulse Per Second (PPS) signal, since this will be offered to the PPS
+ subsystem to synchronize the Linux clock.
+
+Example:
+
+ ptp_clock@24E00 {
+ device_type = "ptp_clock";
+ model = "eTSEC";
+ reg = <0x24E00 0xB0>;
+ interrupts = <12 0x8 13 0x8>;
+ interrupt-parent = < &ipic >;
+ tclk_period = <10>;
+ tmr_prsc = <100>;
+ tmr_add = <0x999999A4>;
+ cksel = <0x1>;
+ tmr_fiper1 = <0x3B9AC9F6>;
+ tmr_fiper2 = <0x00018696>;
+ };
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 183f2aa..6be6b16 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -208,6 +208,20 @@
sleep = <&pmc 0x00300000>;
};
+ ptp_clock@24E00 {
+ device_type = "ptp_clock";
+ model = "eTSEC";
+ reg = <0x24E00 0xB0>;
+ interrupts = <12 0x8 13 0x8>;
+ interrupt-parent = < &ipic >;
+ tclk_period = <10>;
+ tmr_prsc = <100>;
+ tmr_add = <0x999999A4>;
+ cksel = <0x1>;
+ tmr_fiper1 = <0x3B9AC9F6>;
+ tmr_fiper2 = <0x00018696>;
+ };
+
enet0: ethernet@24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
index 1101914..f72353a 100644
--- a/arch/powerpc/boot/dts/p2020ds.dts
+++ b/arch/powerpc/boot/dts/p2020ds.dts
@@ -336,6 +336,20 @@
phy_type = "ulpi";
};
+ ptp_clock@24E00 {
+ device_type = "ptp_clock";
+ model = "eTSEC";
+ reg = <0x24E00 0xB0>;
+ interrupts = <68 2 69 2 70 2>;
+ interrupt-parent = < &mpic >;
+ tclk_period = <5>;
+ tmr_prsc = <200>;
+ tmr_add = <0xCCCCCCCD>;
+ cksel = <1>;
+ tmr_fiper1 = <0x3B9AC9FB>;
+ tmr_fiper2 = <0x0001869B>;
+ };
+
enet0: ethernet@24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts
index da4cb0d..3eb9e65 100644
--- a/arch/powerpc/boot/dts/p2020rdb.dts
+++ b/arch/powerpc/boot/dts/p2020rdb.dts
@@ -396,6 +396,20 @@
phy_type = "ulpi";
};
+ ptp_clock@24E00 {
+ device_type = "ptp_clock";
+ model = "eTSEC";
+ reg = <0x24E00 0xB0>;
+ interrupts = <68 2 69 2 70 2>;
+ interrupt-parent = < &mpic >;
+ tclk_period = <5>;
+ tmr_prsc = <200>;
+ tmr_add = <0xCCCCCCCD>;
+ cksel = <1>;
+ tmr_fiper1 = <0x3B9AC9FB>;
+ tmr_fiper2 = <0x0001869B>;
+ };
+
enet0: ethernet@24000 {
#address-cells = <1>;
#size-cells = <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) += atlx/
obj-$(CONFIG_ATL1E) += atl1e/
obj-$(CONFIG_ATL1C) += atl1c/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
obj-$(CONFIG_TEHUTI) += tehuti.o
obj-$(CONFIG_ENIC) += enic/
obj-$(CONFIG_JME) += jme.o
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
new file mode 100644
index 0000000..46797aa
--- /dev/null
+++ b/drivers/net/gianfar_ptp.c
@@ -0,0 +1,521 @@
+/*
+ * PTP 1588 clock using the eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/timex.h>
+#include <asm/io.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+#include "gianfar_ptp_reg.h"
+#include "gianfar.h"
+
+#define DRIVER "gianfar_ptp"
+#define N_ALARM 1 /* first alarm is used internally to reset fipers */
+#define N_EXT_TS 2
+#define N_IRQS 2
+#define REG_SIZE sizeof(struct gianfar_ptp_registers)
+
+struct etsects {
+ struct gianfar_ptp_registers *regs;
+ int irq[N_IRQS];
+ u64 alarm_interval; /* for periodic alarm */
+ u64 alarm_value;
+ u32 tclk_period; /* nanoseconds */
+ u32 tmr_prsc;
+ u32 tmr_add;
+ u32 cksel;
+ u32 tmr_fiper1;
+ u32 tmr_fiper2;
+};
+
+/* Private globals */
+static struct ptp_clock *gianfar_clock;
+static struct etsects the_clock;
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static u64 tmr_cnt_read(struct etsects *etsects)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ lo = gfar_read(&etsects->regs->tmr_cnt_l);
+ hi = gfar_read(&etsects->regs->tmr_cnt_h);
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ return ns;
+}
+
+static void tmr_cnt_write(struct etsects *etsects, u64 ns)
+{
+ u32 hi = ns >> 32;
+ u32 lo = ns & 0xffffffff;
+
+ gfar_write(&etsects->regs->tmr_cnt_l, lo);
+ gfar_write(&etsects->regs->tmr_cnt_h, hi);
+}
+
+static void set_alarm(struct etsects *etsects)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ ns = tmr_cnt_read(etsects) + 1500000000ULL;
+ ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
+ ns -= etsects->tclk_period;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+ gfar_write(&etsects->regs->tmr_alarm1_l, lo);
+ gfar_write(&etsects->regs->tmr_alarm1_h, hi);
+}
+
+static void set_fipers(struct etsects *etsects)
+{
+ u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
+
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl & (~TE));
+ gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+ gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+ set_alarm(etsects);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|TE);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+ struct etsects *etsects = priv;
+ struct ptp_clock_event event;
+ u64 ns;
+ u32 ack=0, lo, hi, mask, val;
+
+ val = gfar_read(&etsects->regs->tmr_tevent);
+
+ if (val & ETS1) {
+ ack |= ETS1;
+ hi = gfar_read(&etsects->regs->tmr_etts1_h);
+ lo = gfar_read(&etsects->regs->tmr_etts1_l);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 0;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ ptp_clock_event(gianfar_clock, &event);
+ }
+
+ if (val & ETS2) {
+ ack |= ETS2;
+ hi = gfar_read(&etsects->regs->tmr_etts2_h);
+ lo = gfar_read(&etsects->regs->tmr_etts2_l);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 1;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ ptp_clock_event(gianfar_clock, &event);
+ }
+
+ if (val & ALM2) {
+ ack |= ALM2;
+ if (etsects->alarm_value) {
+ event.type = PTP_CLOCK_ALARM;
+ event.index = 0;
+ event.timestamp = etsects->alarm_value;
+ ptp_clock_event(gianfar_clock, &event);
+ }
+ if (etsects->alarm_interval) {
+ ns = etsects->alarm_value + etsects->alarm_interval;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+ spin_lock(®ister_lock);
+ gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+ gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+ spin_unlock(®ister_lock);
+ etsects->alarm_value = ns;
+ } else {
+ gfar_write(&etsects->regs->tmr_tevent, ALM2);
+ spin_lock(®ister_lock);
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ mask &= ~ALM2EN;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ spin_unlock(®ister_lock);
+ etsects->alarm_value = 0;
+ etsects->alarm_interval = 0;
+ }
+ }
+
+ gfar_write(&etsects->regs->tmr_tevent, ack);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_gianfar_adjfreq(void *priv, s32 ppb)
+{
+ u64 adj;
+ u32 diff, tmr_add;
+ int neg_adj = 0;
+ struct etsects *etsects = priv;
+
+ if (!ppb)
+ return 0;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ tmr_add = etsects->tmr_add;
+ adj = tmr_add;
+ adj *= ppb;
+ diff = div_u64(adj, 1000000000ULL);
+
+ tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
+
+ gfar_write(&etsects->regs->tmr_add, tmr_add);
+
+ return 0;
+}
+
+static int ptp_gianfar_adjtime(void *priv, struct timespec *ts)
+{
+ s64 delta, now;
+ unsigned long flags;
+ struct etsects *etsects = priv;
+
+ delta = 1000000000LL * ts->tv_sec;
+ delta += ts->tv_nsec;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ now = tmr_cnt_read(etsects);
+ now += delta;
+ tmr_cnt_write(etsects, now);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ set_fipers(etsects);
+
+ return 0;
+}
+
+static int ptp_gianfar_gettime(void *priv, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ unsigned long flags;
+ struct etsects *etsects = priv;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ ns = tmr_cnt_read(etsects);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_gianfar_settime(void *priv, struct timespec *ts)
+{
+ u64 ns;
+ unsigned long flags;
+ struct etsects *etsects = priv;
+
+ ns = ts->tv_sec * 1000000000ULL;
+ ns += ts->tv_nsec;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ tmr_cnt_write(etsects, ns);
+ set_fipers(etsects);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ return 0;
+}
+
+static int ptp_gianfar_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+ u64 now, ns;
+ u32 remainder;
+ unsigned long flags;
+ struct etsects *etsects = priv;
+
+ ns = etsects->alarm_interval;
+
+ ts->it_interval.tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->it_interval.tv_nsec = remainder;
+
+ spin_lock_irqsave(®ister_lock, flags);
+ now = tmr_cnt_read(etsects);
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ns = etsects->alarm_value - now;
+
+ ts->it_value.tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->it_value.tv_nsec = remainder;
+
+ return 0;
+}
+
+static int ptp_gianfar_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+ u64 ns;
+ u32 lo, hi, mask;
+ unsigned long flags;
+ struct etsects *etsects = p;
+
+ ns = ts->it_interval.tv_sec * 1000000000ULL;
+ ns += ts->it_interval.tv_nsec;
+
+ etsects->alarm_interval = ns;
+
+ ns = ts->it_value.tv_sec * 1000000000ULL;
+ ns += ts->it_value.tv_nsec;
+
+ if (!ns) {
+ /* Cancel the timer. */
+ etsects->alarm_value = 0;
+ etsects->alarm_interval = 0;
+ return 0;
+ }
+
+ if (!abs) {
+ spin_lock_irqsave(®ister_lock, flags);
+ ns += tmr_cnt_read(etsects);
+ spin_unlock_irqrestore(®ister_lock, flags);
+ }
+
+ etsects->alarm_value = ns;
+
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+ gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ mask |= ALM2EN;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ return 0;
+}
+
+static int ptp_gianfar_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+ struct etsects *etsects = priv;
+ unsigned long flags;
+ u32 bit, mask;
+
+ switch (rq->type) {
+ case PTP_REQUEST_EXTTS:
+ switch (rq->index) {
+ case 0:
+ bit = ETS1EN;
+ break;
+ case 1:
+ bit = ETS2EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ spin_lock_irqsave(®ister_lock, flags);
+ if (on) {
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ mask |= bit;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ } else {
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ mask &= ~bit;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ }
+ spin_unlock_irqrestore(®ister_lock, flags);
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_gianfar_caps = {
+ .owner = THIS_MODULE,
+ .name = "gianfar clock",
+ .max_adj = 512000,
+ .n_alarm = N_ALARM,
+ .n_ext_ts = N_EXT_TS,
+ .n_per_out = 0,
+ .pps = 0,
+ .priv = &the_clock,
+ .adjfreq = ptp_gianfar_adjfreq,
+ .adjtime = ptp_gianfar_adjtime,
+ .gettime = ptp_gianfar_gettime,
+ .settime = ptp_gianfar_settime,
+ .gettimer = ptp_gianfar_gettimer,
+ .settimer = ptp_gianfar_settimer,
+ .enable = ptp_gianfar_enable,
+};
+
+/* OF device tree */
+
+static int get_of_u32(struct device_node *node, char *str, u32 *val)
+{
+ int plen;
+ const u32 *prop = of_get_property(node, str, &plen);
+
+ if (!prop || plen != sizeof(*prop))
+ return -1;
+ *val = *prop;
+ return 0;
+}
+
+static int gianfar_ptp_probe(struct of_device* dev,
+ const struct of_device_id *match)
+{
+ u64 addr, size;
+ struct device_node *node = dev->node;
+ struct etsects *etsects = &the_clock;
+ struct timespec now;
+ phys_addr_t reg_addr;
+ unsigned long reg_size;
+ u32 tmr_ctrl;
+ int i;
+
+ if (get_of_u32(node, "tclk_period", &etsects->tclk_period) ||
+ get_of_u32(node, "tmr_prsc", &etsects->tmr_prsc) ||
+ get_of_u32(node, "tmr_add", &etsects->tmr_add) ||
+ get_of_u32(node, "cksel", &etsects->cksel) ||
+ get_of_u32(node, "tmr_fiper1", &etsects->tmr_fiper1) ||
+ get_of_u32(node, "tmr_fiper2", &etsects->tmr_fiper2))
+ return -ENODEV;
+
+ for (i = 0; i < N_IRQS; i++) {
+
+ etsects->irq[i] = irq_of_parse_and_map(node, i);
+
+ if (etsects->irq[i] == NO_IRQ) {
+ pr_err("irq[%d] not in device tree", i);
+ return -ENODEV;
+ }
+
+ if (request_irq(etsects->irq[i], isr, 0, DRIVER, etsects)) {
+ pr_err("request_irq failed irq %d", etsects->irq[i]);
+ return -ENODEV;
+ }
+ }
+
+ addr = of_translate_address(node, of_get_address(node, 0, &size, NULL));
+ reg_addr = addr;
+ reg_size = size;
+ if (reg_size < REG_SIZE) {
+ pr_warning("device tree reg range %lu too small\n", reg_size);
+ reg_size = REG_SIZE;
+ }
+ etsects->regs = ioremap(reg_addr, reg_size);
+ if (!etsects->regs) {
+ pr_err("ioremap ptp registers failed\n");
+ return -EINVAL;
+ }
+
+ tmr_ctrl =
+ (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
+ (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
+
+ getnstimeofday(&now);
+ ptp_gianfar_settime(etsects, &now);
+
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl);
+ gfar_write(&etsects->regs->tmr_add, etsects->tmr_add);
+ gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+ gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+ set_alarm(etsects);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE);
+
+ gianfar_clock = ptp_clock_register(&ptp_gianfar_caps);
+
+ return IS_ERR(gianfar_clock) ? PTR_ERR(gianfar_clock) : 0;
+}
+
+static int gianfar_ptp_remove(struct of_device* dev)
+{
+ int i;
+
+ gfar_write(&the_clock.regs->tmr_temask, 0);
+ gfar_write(&the_clock.regs->tmr_ctrl, 0);
+
+ ptp_clock_unregister(gianfar_clock);
+
+ for (i = 0; i < N_IRQS; i++)
+ free_irq(the_clock.irq[i], &the_clock);
+
+ iounmap(the_clock.regs);
+
+ return 0;
+}
+
+static struct of_device_id match_table[] = {
+ { .type = "ptp_clock" },
+ {},
+};
+
+static struct of_platform_driver gianfar_ptp_driver = {
+ .name = "gianfar_ptp",
+ .match_table = match_table,
+ .owner = THIS_MODULE,
+ .probe = gianfar_ptp_probe,
+ .remove = gianfar_ptp_remove,
+};
+
+/* module operations */
+
+static void __exit ptp_gianfar_exit(void)
+{
+ of_unregister_platform_driver(&gianfar_ptp_driver);
+}
+
+static int __init ptp_gianfar_init(void)
+{
+ return of_register_platform_driver(&gianfar_ptp_driver);
+}
+
+module_init(ptp_gianfar_init);
+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..6e2b82f
--- /dev/null
+++ b/drivers/net/gianfar_ptp_reg.h
@@ -0,0 +1,113 @@
+/* gianfar_ptp_reg.h
+ * Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010
+ *
+ * PTP 1588 clock using the gianfar eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GIANFAR_PTP_REG_H_
+#define _GIANFAR_PTP_REG_H_
+
+struct gianfar_ptp_registers {
+ u32 tmr_ctrl; /* Timer control register */
+ u32 tmr_tevent; /* Timestamp event register */
+ u32 tmr_temask; /* Timer event mask register */
+ u32 tmr_pevent; /* Timestamp event register */
+ u32 tmr_pemask; /* Timer event mask register */
+ u32 tmr_stat; /* Timestamp status register */
+ u32 tmr_cnt_h; /* Timer counter high register */
+ u32 tmr_cnt_l; /* Timer counter low register */
+ u32 tmr_add; /* Timer drift compensation addend register */
+ u32 tmr_acc; /* Timer accumulator register */
+ u32 tmr_prsc; /* Timer prescale */
+ u8 res1[4];
+ u32 tmroff_h; /* Timer offset high */
+ u32 tmroff_l; /* Timer offset low */
+ u8 res2[8];
+ u32 tmr_alarm1_h; /* Timer alarm 1 high register */
+ u32 tmr_alarm1_l; /* Timer alarm 1 high register */
+ u32 tmr_alarm2_h; /* Timer alarm 2 high register */
+ u32 tmr_alarm2_l; /* Timer alarm 2 high register */
+ u8 res3[48];
+ u32 tmr_fiper1; /* Timer fixed period interval */
+ u32 tmr_fiper2; /* Timer fixed period interval */
+ u32 tmr_fiper3; /* Timer fixed period interval */
+ u8 res4[20];
+ u32 tmr_etts1_h; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts1_l; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts2_h; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts2_l; /* Timestamp of general purpose external trigger */
+};
+
+/* Bit definitions for the TMR_CTRL register */
+#define ALM1P (1<<31) /* Alarm1 output polarity */
+#define ALM2P (1<<30) /* Alarm2 output polarity */
+#define FS (1<<28) /* FIPER start indication */
+#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */
+#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */
+#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */
+#define TCLK_PERIOD_MASK (0x3ff)
+#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */
+#define FRD (1<<14) /* FIPER Realignment Disable */
+#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */
+#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */
+#define ETEP2 (1<<9) /* External trigger 2 edge polarity */
+#define ETEP1 (1<<8) /* External trigger 1 edge polarity */
+#define COPH (1<<7) /* Generated clock (TSEC_1588_GCLK) output phase. */
+#define CIPH (1<<6) /* External oscillator input clock phase. */
+#define TMSR (1<<5) /* Timer soft reset. When enabled, it resets all the timer registers and state machines. */
+#define BYP (1<<3) /* Bypass drift compensated clock */
+#define TE (1<<2) /* 1588 timer enable. If not enabled, all the timer registers and state machines are disabled. */
+#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source select. */
+#define CKSEL_MASK (0x3)
+
+/* Bit definitions for the TMR_TEVENT register */
+#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */
+#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */
+#define ALM2 (1<<17) /* Current time equaled alarm time register 2 */
+#define ALM1 (1<<16) /* Current time equaled alarm time register 1 */
+#define PP1 (1<<7) /* Indicates that a periodic pulse has been generated based on FIPER1 register */
+#define PP2 (1<<6) /* Indicates that a periodic pulse has been generated based on FIPER2 register */
+#define PP3 (1<<5) /* Indicates that a periodic pulse has been generated based on FIPER3 register */
+
+/* Bit definitions for the TMR_TEMASK register */
+#define ETS2EN (1<<25) /* External trigger 2 timestamp sample event enable */
+#define ETS1EN (1<<24) /* External trigger 1 timestamp sample event enable */
+#define ALM2EN (1<<17) /* Timer ALM2 event enable */
+#define ALM1EN (1<<16) /* Timer ALM1 event enable */
+#define PP1EN (1<<7) /* Periodic pulse event 1 enable */
+#define PP2EN (1<<6) /* Periodic pulse event 2 enable */
+
+/* Bit definitions for the TMR_PEVENT register */
+#define TXP2 (1<<9) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS2 register */
+#define TXP1 (1<<8) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS1 register */
+#define RXP (1<<0) /* Indicates that a PTP frame has been received */
+
+/* Bit definitions for the TMR_PEMASK register */
+#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */
+#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */
+#define RXPEN (1<<0) /* Receive PTP packet event enable */
+
+/* Bit definitions for the TMR_STAT register */
+#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */
+#define STAT_VEC_MASK (0x3f)
+
+/* Bit definitions for the TMR_PRSC register */
+#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */
+#define PRSC_OCK_MASK (0xffff)
+
+#endif
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 9390d44..3b7bd73 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -35,4 +35,17 @@ config PTP_1588_CLOCK_LINUX
To compile this driver as a module, choose M here: the module
will be called ptp_linux.
+config PTP_1588_CLOCK_GIANFAR
+ tristate "Freescale eTSEC as PTP clock"
+ depends on PTP_1588_CLOCK
+ depends on GIANFAR
+ help
+ This driver adds support for using the eTSEC as a PTP
+ clock. This clock is only useful if your PTP programs are
+ getting hardware time stamps on the PTP Ethernet packets
+ using the SO_TIMESTAMPING API.
+
+ To compile this driver as a module, choose M here: the module
+ will be called gianfar_ptp.
+
endmenu
--
1.6.3.3
^ permalink raw reply related
* [PATCH v3 2/3] ptp: Added a clock that uses the Linux system time.
From: Richard Cochran @ 2010-05-14 16:46 UTC (permalink / raw)
To: netdev; +Cc: devicetree-discuss, linuxppc-dev
In-Reply-To: <cover.1273855016.git.richard.cochran@omicron.at>
This PTP clock simply uses the NTP time adjustment system calls. The
driver can be used in systems that lack a special hardware PTP clock.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
drivers/ptp/Kconfig | 12 ++++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_linux.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++
kernel/time/ntp.c | 2 +
4 files changed, 151 insertions(+), 0 deletions(-)
create mode 100644 drivers/ptp/ptp_linux.c
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index c80a25b..9390d44 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -23,4 +23,16 @@ config PTP_1588_CLOCK
To compile this driver as a module, choose M here: the module
will be called ptp_clock.
+config PTP_1588_CLOCK_LINUX
+ tristate "Linux system timer as PTP clock"
+ depends on PTP_1588_CLOCK
+ help
+ This driver adds support for using the standard Linux time
+ source as a PTP clock. This clock is only useful if your PTP
+ programs are using software time stamps for the PTP Ethernet
+ packets.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_linux.
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index b86695c..1651d52 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_PTP_1588_CLOCK) += ptp_clock.o
+obj-$(CONFIG_PTP_1588_CLOCK_LINUX) += ptp_linux.o
diff --git a/drivers/ptp/ptp_linux.c b/drivers/ptp/ptp_linux.c
new file mode 100644
index 0000000..f0dbfc6
--- /dev/null
+++ b/drivers/ptp/ptp_linux.c
@@ -0,0 +1,136 @@
+/*
+ * PTP 1588 clock using the Linux system clock
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timex.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+static struct ptp_clock *linux_clock;
+
+DEFINE_SPINLOCK(adjtime_lock);
+
+static int ptp_linux_adjfreq(void *priv, s32 delta)
+{
+ struct timex txc;
+ s64 tmp = delta;
+ int err;
+ txc.freq = div_s64(tmp<<16, 1000);
+ txc.modes = ADJ_FREQUENCY;
+ err = do_adjtimex(&txc);
+ return err < 0 ? err : 0;
+}
+
+static int ptp_linux_adjtime(void *priv, struct timespec *ts)
+{
+ s64 delta;
+ ktime_t now;
+ struct timespec t2;
+ unsigned long flags;
+ int err;
+
+ delta = 1000000000LL * ts->tv_sec + ts->tv_nsec;
+
+ spin_lock_irqsave(&adjtime_lock, flags);
+
+ now = ktime_get_real();
+
+ now = delta < 0 ? ktime_sub_ns(now,-delta) : ktime_add_ns(now,delta);
+
+ t2 = ktime_to_timespec(now);
+
+ err = do_settimeofday(&t2);
+
+ spin_unlock_irqrestore(&adjtime_lock, flags);
+
+ return err;
+}
+
+static int ptp_linux_gettime(void *priv, struct timespec *ts)
+{
+ getnstimeofday(ts);
+ return 0;
+}
+
+static int ptp_linux_settime(void *priv, struct timespec *ts)
+{
+ return do_settimeofday(ts);
+}
+
+static int ptp_linux_gettimer(void *priv, int index, struct itimerspec *ts)
+{
+ /* We do not offer any ancillary features at all. */
+ return -EOPNOTSUPP;
+}
+
+static int ptp_linux_settimer(void *p, int i, int abs, struct itimerspec *ts)
+{
+ /* We do not offer any ancillary features at all. */
+ return -EOPNOTSUPP;
+}
+
+static int ptp_linux_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+ /* We do not offer any ancillary features at all. */
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_linux_caps = {
+ .owner = THIS_MODULE,
+ .name = "Linux timer",
+ .max_adj = 512000,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .pps = 0,
+ .priv = NULL,
+ .adjfreq = ptp_linux_adjfreq,
+ .adjtime = ptp_linux_adjtime,
+ .gettime = ptp_linux_gettime,
+ .settime = ptp_linux_settime,
+ .gettimer = ptp_linux_gettimer,
+ .settimer = ptp_linux_settimer,
+ .enable = ptp_linux_enable,
+};
+
+/* module operations */
+
+static void __exit ptp_linux_exit(void)
+{
+ ptp_clock_unregister(linux_clock);
+}
+
+static int __init ptp_linux_init(void)
+{
+ linux_clock = ptp_clock_register(&ptp_linux_caps);
+
+ return IS_ERR(linux_clock) ? PTR_ERR(linux_clock) : 0;
+}
+
+module_init(ptp_linux_init);
+module_exit(ptp_linux_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the Linux system timer");
+MODULE_LICENSE("GPL");
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 7c0f180..efd1ff8 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -14,6 +14,7 @@
#include <linux/timex.h>
#include <linux/time.h>
#include <linux/mm.h>
+#include <linux/module.h>
/*
* NTP timekeeping variables:
@@ -535,6 +536,7 @@ int do_adjtimex(struct timex *txc)
return result;
}
+EXPORT_SYMBOL(do_adjtimex);
static int __init ntp_tick_adj_setup(char *str)
{
--
1.6.3.3
^ permalink raw reply related
* [PATCH v3 1/3] ptp: Added a brand new class driver for ptp clocks.
From: Richard Cochran @ 2010-05-14 16:45 UTC (permalink / raw)
To: netdev; +Cc: devicetree-discuss, linuxppc-dev
In-Reply-To: <cover.1273855016.git.richard.cochran@omicron.at>
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>
---
Documentation/ptp/ptp.txt | 95 +++++++
Documentation/ptp/testptp.c | 245 ++++++++++++++++++
Documentation/ptp/testptp.mk | 33 +++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ptp/Kconfig | 26 ++
drivers/ptp/Makefile | 5 +
drivers/ptp/ptp_clock.c | 512 ++++++++++++++++++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 79 ++++++
include/linux/ptp_clock_kernel.h | 137 ++++++++++
11 files changed, 1136 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ptp/ptp.txt
create mode 100644 Documentation/ptp/testptp.c
create mode 100644 Documentation/ptp/testptp.mk
create mode 100644 drivers/ptp/Kconfig
create mode 100644 drivers/ptp/Makefile
create mode 100644 drivers/ptp/ptp_clock.c
create mode 100644 include/linux/ptp_clock.h
create mode 100644 include/linux/ptp_clock_kernel.h
diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
new file mode 100644
index 0000000..46858b3
--- /dev/null
+++ b/Documentation/ptp/ptp.txt
@@ -0,0 +1,95 @@
+
+* PTP infrastructure for Linux
+
+ This patch set introduces support for IEEE 1588 PTP clocks in
+ Linux. Together with the SO_TIMESTAMPING socket options, this
+ presents a standardized method for developing PTP user space
+ programs, synchronizing Linux with external clocks, and using the
+ ancillary features of PTP hardware clocks.
+
+ A new class driver exports a kernel interface for specific clock
+ drivers and a user space interface. The infrastructure supports a
+ complete set of PTP functionality.
+
+ + Basic clock operations
+ - Set time
+ - Get time
+ - Shift the clock by a given offset atomically
+ - Adjust clock frequency
+
+ + Ancillary clock features
+ - One short or periodic alarms, with signal delivery to user program
+ - Time stamp external events
+ - Period output signals configurable from user space
+ - Synchronization of the Linux system time via the PPS subsystem
+
+** PTP kernel API
+
+ A PTP clock driver registers itself with the class driver. The
+ class driver handles all of the dealings with user space. The
+ author of a clock driver need only implement the details of
+ programming the clock hardware. The clock driver notifies the class
+ driver of asynchronous events (alarms and external time stamps) via
+ a simple message passing interface.
+
+ The class driver supports multiple PTP clock drivers. In normal use
+ cases, only one PTP clock is needed. However, for testing and
+ development, it can be useful to have more than one clock in a
+ single system, in order to allow performance comparisons.
+
+** PTP user space API
+
+ The class driver creates a character device for each registered PTP
+ clock. User space programs may control the clock using standardized
+ ioctls. A program may query, enable, configure, and disable the
+ ancillary clock features. User space can receive time stamped
+ events via blocking read() and poll(). One shot and periodic
+ signals may be configured via an ioctl API with semantics similar
+ to the POSIX timer_settime() system call.
+
+ As an real life example, the following two patches for ptpd version
+ 1.0.0 demonstrate how the API works.
+
+ https://sourceforge.net/tracker/?func=detail&aid=2992845&group_id=139814&atid=744634
+
+ https://sourceforge.net/tracker/?func=detail&aid=2992847&group_id=139814&atid=744634
+
+** Writing clock drivers
+
+ Clock drivers include include/linux/ptp_clock_kernel.h and register
+ themselves by presenting a 'struct ptp_clock_info' to the
+ registration method. Clock drivers must implement all of the
+ functions in the interface. If a clock does not offer a particular
+ ancillary feature, then the driver should just return -EOPNOTSUPP
+ from those functions.
+
+ Drivers must ensure that all of the methods in interface are
+ reentrant. Since most hardware implementations treat the time value
+ as a 64 bit integer accessed as two 32 bit registers, drivers
+ should use spin_lock_irqsave/spin_unlock_irqrestore to protect
+ against concurrent access. This locking cannot be accomplished in
+ class driver, since the lock may also be needed by the clock
+ driver's interrupt service routine.
+
+** Supported hardware
+
+ + Standard Linux system timer
+ - No special PTP features
+ - For use with software time stamping
+
+ + Freescale eTSEC gianfar
+ - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
+ - 2 Alarm registers (optional interrupt)
+ - 3 Periodic signals (optional interrupt)
+
+ + National DP83640
+ - 6 GPIOs programmable as inputs or outputs
+ - 6 GPIOs with dedicated functions (LED/JTAG/clock) can also be
+ used as general inputs or outputs
+ - GPIO inputs can time stamp external triggers
+ - GPIO outputs can produce periodic signals
+ - 1 interrupt pin
+
+ + Intel IXP465
+ - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
+ - Target Time (optional interrupt)
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
new file mode 100644
index 0000000..ed2ceea
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,245 @@
+/*
+ * PTP 1588 clock support - User space test program
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/ptp_clock.h>
+
+static void handle_alarm(int s)
+{
+ printf("received signal %d \n", s);
+}
+
+static int install_handler(int signum, void (*handler)(int))
+{
+ struct sigaction action;
+ sigset_t mask;
+
+ /* Unblock the signal. */
+ sigemptyset(&mask);
+ sigaddset(&mask, signum);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ /* Install the signal handler. */
+ action.sa_handler = handler;
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ sigaction(signum, &action, NULL);
+
+ return 0;
+}
+
+static void usage (char* progname)
+{
+ fprintf(stderr,
+ "usage: %s [options] \n"
+ " -a val request a one-shot alarm after 'val' seconds \n"
+ " -A val request a periodic alarm every 'val' seconds \n"
+ " -c query the ptp clock's capabilities \n"
+ " -d name device to open \n"
+ " -e val read 'val' external time stamp events \n"
+ " -f val adjust the ptp clock frequency by 'val' PPB \n"
+ " -g get the ptp clock time \n"
+ " -h prints this message \n"
+ " -s set the ptp clock time from the system time \n"
+ " -t val shift the ptp clock time by 'val' seconds \n"
+ " -v query the ptp clock api version \n"
+ ,progname);
+}
+
+int main(int argc,char *argv[])
+{
+ struct ptp_clock_caps caps;
+ struct ptp_clock_timer timer;
+ struct ptp_extts_event event;
+ struct ptp_clock_request request;
+ struct timespec ts;
+ char *progname;
+ int c, cnt, fd, val=0;
+
+ char *device = "/dev/ptp_clock_0";
+ int adjfreq=0x7fffffff;
+ int adjtime=0;
+ int capabilities=0;
+ int extts=0;
+ int gettime=0;
+ int oneshot=0;
+ int periodic=0;
+ int settime=0;
+ int version=0;
+
+ progname = strrchr(argv[0],'/');
+ progname = progname ? 1+progname : argv[0];
+ while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghst:v"))) {
+ switch (c) {
+ case 'a': oneshot = atoi(optarg); break;
+ case 'A': periodic = atoi(optarg); break;
+ case 'c': capabilities = 1; break;
+ case 'd': device = optarg; break;
+ case 'e': extts = atoi(optarg); break;
+ case 'f': adjfreq = atoi(optarg); break;
+ case 'g': gettime = 1; break;
+ case 's': settime = 1; break;
+ case 't': adjtime = atoi(optarg); break;
+ case 'v': version = 1; break;
+ case 'h': usage(progname); return 0;
+ case '?': usage(progname); return -1;
+ default: usage(progname); return -1;
+ }
+ }
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr,"cannot open %s: %s", device, strerror(errno));
+ return -1;
+ }
+
+ if (version) {
+ if (ioctl(fd, PTP_CLOCK_APIVERS, &val)) {
+ perror("PTP_CLOCK_APIVERS");
+ } else {
+ printf("version = 0x%08x \n",val);
+ }
+ }
+
+ if (capabilities) {
+ if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+ perror("PTP_CLOCK_GETCAPS");
+ } else {
+ printf("capabilities: \n"
+ " %d maximum frequency adjustment (PPB) \n"
+ " %d programmable alarms \n"
+ " %d external time stamp channels \n"
+ " %d programmable periodic signals \n"
+ " %d pulse per second \n",
+ caps.max_adj,
+ caps.n_alarm,
+ caps.n_ext_ts,
+ caps.n_per_out,
+ caps.pps);
+ }
+ }
+
+ if (0x7fffffff != adjfreq) {
+ if (ioctl(fd, PTP_CLOCK_ADJFREQ, adjfreq)) {
+ perror("PTP_CLOCK_ADJFREQ");
+ } else {
+ puts("frequency adjustment okay");
+ }
+ }
+
+ if (adjtime) {
+ ts.tv_sec = adjtime;
+ ts.tv_nsec = 0;
+ if (ioctl(fd, PTP_CLOCK_ADJTIME, &ts)) {
+ perror("PTP_CLOCK_ADJTIME");
+ } else {
+ puts("time shift okay");
+ }
+ }
+
+ if (gettime) {
+ if (ioctl(fd, PTP_CLOCK_GETTIME, &ts)) {
+ perror("PTP_CLOCK_GETTIME");
+ } else {
+ printf("clock time: %ld.%09ld or %s",
+ ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
+ }
+ }
+
+ if (settime) {
+ clock_gettime(CLOCK_REALTIME, &ts);
+ if (ioctl(fd, PTP_CLOCK_SETTIME, &ts)) {
+ perror("PTP_CLOCK_SETTIME");
+ } else {
+ puts("set time okay");
+ }
+ }
+
+ if (extts) {
+ memset(&request, 0, sizeof(request));
+ request.type = PTP_REQUEST_EXTTS;
+ request.index = 0;
+ request.flags = PTP_ENABLE_FEATURE;
+ if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+ perror("PTP_FEATURE_REQUEST");
+ extts = 0;
+ } else {
+ puts("set timer okay");
+ }
+ for (; extts; extts--) {
+ cnt = read(fd, &event, sizeof(event));
+ if (cnt != sizeof(event)) {
+ perror("read");
+ break;
+ }
+ printf("event index %d at %ld.%09ld \n", event.index,
+ event.ts.tv_sec, event.ts.tv_nsec);
+ }
+ /* Disable the feature again. */
+ request.flags = 0;
+ if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+ perror("PTP_FEATURE_REQUEST");
+ }
+ }
+
+ if (oneshot) {
+ install_handler(SIGALRM, handle_alarm);
+ memset(&timer, 0, sizeof(timer));
+ timer.signum = SIGALRM;
+ timer.tsp.it_value.tv_sec = oneshot;
+ if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
+ perror("PTP_CLOCK_SETTIMER");
+ } else {
+ puts("set timer okay");
+ }
+ pause();
+ }
+
+ if (periodic) {
+ install_handler(SIGALRM, handle_alarm);
+ memset(&timer, 0, sizeof(timer));
+ timer.signum = SIGALRM;
+ timer.tsp.it_value.tv_sec = periodic;
+ timer.tsp.it_interval.tv_sec = periodic;
+ if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
+ perror("PTP_CLOCK_SETTIMER");
+ } else {
+ puts("set timer okay");
+ }
+ while (1) {
+ pause();
+ }
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
new file mode 100644
index 0000000..4ef2d97
--- /dev/null
+++ b/Documentation/ptp/testptp.mk
@@ -0,0 +1,33 @@
+# PTP 1588 clock support - User space test program
+#
+# Copyright (C) 2010 OMICRON electronics GmbH
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CC = $(CROSS_COMPILE)gcc
+INC = -I$(KBUILD_OUTPUT)/usr/include
+CFLAGS = -Wall $(INC)
+LDLIBS = -lrt
+PROGS = testptp
+
+all: $(PROGS)
+
+testptp: testptp.o
+
+clean:
+ rm -f testptp.o
+
+distclean: clean
+ rm -f $(PROGS)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f..774fbd7 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@ source "drivers/spi/Kconfig"
source "drivers/pps/Kconfig"
+source "drivers/ptp/Kconfig"
+
source "drivers/gpio/Kconfig"
source "drivers/w1/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index f42a030..84228bc 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_RTC_LIB) += rtc/
obj-y += i2c/ media/
obj-$(CONFIG_PPS) += pps/
+obj-$(CONFIG_PTP_1588_CLOCK) += ptp/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_HWMON) += hwmon/
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
new file mode 100644
index 0000000..c80a25b
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,26 @@
+#
+# PTP clock support configuration
+#
+
+menu "PTP clock support"
+
+config PTP_1588_CLOCK
+ tristate "PTP clock support"
+ depends on EXPERIMENTAL
+ help
+ The IEEE 1588 standard defines a method to precisely
+ synchronize distributed clocks over Ethernet networks. The
+ standard defines a Precision Time Protocol (PTP), which can
+ be used to achieve synchronization within a few dozen
+ microseconds. In addition, with the help of special hardware
+ time stamping units, it can be possible to achieve
+ synchronization to within a few hundred nanoseconds.
+
+ This driver adds support for PTP clocks as character
+ devices. If you want to use a PTP clock, then you should
+ also enable at least one clock driver as well.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_clock.
+
+endmenu
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) += ptp_clock.o
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..1d94d31
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,512 @@
+/*
+ * PTP 1588 clock support
+ *
+ * Partially adapted from the Linux PPS driver.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/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 {
+ struct pid *pid;
+ int sig;
+};
+
+struct timestamp_event_queue {
+ struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
+ int head;
+ int tail;
+ int overflow;
+};
+
+struct ptp_clock {
+ struct list_head list;
+ struct cdev cdev;
+ struct device *dev;
+ struct ptp_clock_info *info;
+ dev_t devid;
+ int index; /* index into clocks.map, also the minor number */
+
+ struct alarm alarm[PTP_MAX_ALARMS];
+ struct mutex alarm_mux; /* one process at a time setting an alarm */
+
+ struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+ struct mutex tsevq_mux; /* one process at a time reading the fifo */
+ 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 {
+ struct list_head list;
+ DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
+} clocks;
+static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
+
+/* time stamp event queue operations */
+
+static inline int queue_cnt(struct timestamp_event_queue *q)
+{
+ int cnt = q->tail - q->head;
+ return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
+}
+
+static inline int queue_free(struct timestamp_event_queue *q)
+{
+ return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
+}
+
+static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
+ struct ptp_clock_event *src)
+{
+ struct ptp_extts_event *dst;
+ u32 remainder;
+
+ dst = &queue->buf[ queue->tail ];
+
+ dst->index = src->index;
+ dst->ts.tv_sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
+ dst->ts.tv_nsec = remainder;
+
+ if (!queue_free(queue))
+ queue->overflow++;
+
+ queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
+}
+
+/* public interface */
+
+struct ptp_clock* ptp_clock_register(struct ptp_clock_info *info)
+{
+ struct ptp_clock *ptp;
+ int err = 0, index, major = MAJOR(ptp_devt);
+ unsigned long flags;
+
+ if (info->n_alarm > PTP_MAX_ALARMS)
+ return ERR_PTR(-EINVAL);
+
+ /* Find a free clock slot and reserve it. */
+ err = -EBUSY;
+ spin_lock_irqsave(&clocks_lock, flags);
+ index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
+ if (index < PTP_MAX_CLOCKS) {
+ set_bit(index, clocks.map);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ goto no_clock;
+ }
+
+ /* Initialize a clock structure. */
+ err = -ENOMEM;
+ ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
+ if (ptp == NULL)
+ goto no_memory;
+
+ ptp->info = info;
+ ptp->devid = MKDEV(major, index);
+ ptp->index = index;
+ mutex_init(&ptp->alarm_mux);
+ mutex_init(&ptp->tsevq_mux);
+ init_waitqueue_head(&ptp->tsev_wq);
+
+ /* Create a new device in our class. */
+ ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
+ "ptp_clock_%d", ptp->index);
+ if (IS_ERR(ptp->dev))
+ goto no_device;
+
+ dev_set_drvdata(ptp->dev, ptp);
+
+ /* Register a character device. */
+ cdev_init(&ptp->cdev, &ptp_fops);
+ ptp->cdev.owner = info->owner;
+ err = cdev_add(&ptp->cdev, ptp->devid, 1);
+ if (err)
+ goto no_cdev;
+
+ /* Clock is ready, add it into the list. */
+ spin_lock_irqsave(&clocks_lock, flags);
+ list_add(&ptp->list, &clocks.list);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ return ptp;
+
+no_cdev:
+ device_destroy(ptp_class, ptp->devid);
+no_device:
+ mutex_destroy(&ptp->alarm_mux);
+ mutex_destroy(&ptp->tsevq_mux);
+ kfree(ptp);
+no_memory:
+ spin_lock_irqsave(&clocks_lock, flags);
+ clear_bit(index, clocks.map);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+no_clock:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+ unsigned long flags;
+
+ /* Release the clock's resources. */
+ cdev_del(&ptp->cdev);
+ device_destroy(ptp_class, ptp->devid);
+ mutex_destroy(&ptp->alarm_mux);
+ mutex_destroy(&ptp->tsevq_mux);
+
+ /* Remove the clock from the list. */
+ spin_lock_irqsave(&clocks_lock, flags);
+ list_del(&ptp->list);
+ clear_bit(ptp->index, clocks.map);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ kfree(ptp);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptp_clock_unregister);
+
+void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+ switch (event->type) {
+
+ case PTP_CLOCK_ALARM:
+ kill_pid(ptp->alarm[ event->index ].pid,
+ ptp->alarm[ event->index ].sig, 1);
+ break;
+
+ case PTP_CLOCK_EXTTS:
+ enqueue_external_timestamp(&ptp->tsevq, event);
+ wake_up_interruptible(&ptp->tsev_wq);
+ break;
+
+ case PTP_CLOCK_PPS:
+ break;
+ }
+}
+EXPORT_SYMBOL(ptp_clock_event);
+
+/* character device operations */
+
+static int ptp_ioctl(struct inode *node, struct file *fp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ptp_clock_caps caps;
+ struct ptp_clock_request req;
+ struct ptp_clock_timer timer;
+ struct ptp_clock *ptp = fp->private_data;
+ struct ptp_clock_info *ops = ptp->info;
+ void *priv = ops->priv;
+ struct timespec ts;
+ int flags, index;
+ int err = 0;
+
+ switch (cmd) {
+
+ case PTP_CLOCK_APIVERS:
+ err = put_user(PTP_CLOCK_VERSION, (u32 __user*)arg);
+ break;
+
+ case PTP_CLOCK_ADJFREQ:
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ err = ops->adjfreq(priv, arg);
+ break;
+
+ case PTP_CLOCK_ADJTIME:
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ if (copy_from_user(&ts, (void __user*)arg, sizeof(ts)))
+ err = -EFAULT;
+ else
+ err = ops->adjtime(priv, &ts);
+ break;
+
+ case PTP_CLOCK_GETTIME:
+ err = ops->gettime(priv, &ts);
+ if (err)
+ break;
+ err = copy_to_user((void __user*)arg, &ts, sizeof(ts));
+ break;
+
+ case PTP_CLOCK_SETTIME:
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ if (copy_from_user(&ts, (void __user*)arg, sizeof(ts)))
+ err = -EFAULT;
+ else
+ err = ops->settime(priv, &ts);
+ break;
+
+ case PTP_CLOCK_GETCAPS:
+ memset(&caps, 0, sizeof(caps));
+ caps.max_adj = ptp->info->max_adj;
+ caps.n_alarm = ptp->info->n_alarm;
+ caps.n_ext_ts = ptp->info->n_ext_ts;
+ caps.n_per_out = ptp->info->n_per_out;
+ caps.pps = ptp->info->pps;
+ err = copy_to_user((void __user*)arg, &caps, sizeof(caps));
+ break;
+
+ case PTP_CLOCK_GETTIMER:
+ if (copy_from_user(&timer, (void __user*)arg, sizeof(timer))) {
+ err = -EFAULT;
+ break;
+ }
+ index = timer.alarm_index;
+ if (index < 0 || index >= ptp->info->n_alarm) {
+ err = -EINVAL;
+ break;
+ }
+ err = ops->gettimer(priv, index, &timer.tsp);
+ if (err)
+ break;
+ err = copy_to_user((void __user*)arg, &timer, sizeof(timer));
+ break;
+
+ case PTP_CLOCK_SETTIMER:
+ if (copy_from_user(&timer, (void __user*)arg, sizeof(timer))) {
+ err = -EFAULT;
+ break;
+ }
+ index = timer.alarm_index;
+ if (index < 0 || index >= ptp->info->n_alarm) {
+ err = -EINVAL;
+ break;
+ }
+ if (!valid_signal(timer.signum))
+ return -EINVAL;
+ flags = timer.flags;
+ if (flags & (flags != TIMER_ABSTIME)) {
+ err = -EINVAL;
+ break;
+ }
+ if (mutex_lock_interruptible(&ptp->alarm_mux))
+ return -ERESTARTSYS;
+
+ if (ptp->alarm[index].pid)
+ put_pid(ptp->alarm[index].pid);
+
+ ptp->alarm[index].pid = get_pid(task_pid(current));
+ ptp->alarm[index].sig = timer.signum;
+ err = ops->settimer(priv, index, flags, &timer.tsp);
+
+ mutex_unlock(&ptp->alarm_mux);
+ break;
+
+ case PTP_FEATURE_REQUEST:
+ if (copy_from_user(&req, (void __user*)arg, sizeof(req))) {
+ err = -EFAULT;
+ break;
+ }
+ switch (req.type) {
+ case PTP_REQUEST_EXTTS:
+ case PTP_REQUEST_PEROUT:
+ break;
+ case PTP_REQUEST_PPS:
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ if (err)
+ break;
+ err = ops->enable(priv, &req,
+ req.flags & PTP_ENABLE_FEATURE ? 1 : 0);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+ return err;
+}
+
+static int ptp_open(struct inode *inode, struct file *fp)
+{
+ struct ptp_clock *ptp;
+ ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
+
+ fp->private_data = ptp;
+
+ return 0;
+}
+
+static unsigned int ptp_poll(struct file *fp, poll_table *wait)
+{
+ struct ptp_clock *ptp = fp->private_data;
+
+ poll_wait(fp, &ptp->tsev_wq, wait);
+
+ return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
+}
+
+static ssize_t ptp_read(struct file *fp, char __user *buf,
+ size_t cnt, loff_t *off)
+{
+ struct ptp_clock *ptp = fp->private_data;
+ struct timestamp_event_queue *queue = &ptp->tsevq;
+ struct ptp_extts_event *event;
+ size_t qcnt;
+
+ if (mutex_lock_interruptible(&ptp->tsevq_mux))
+ return -ERESTARTSYS;
+
+ cnt = cnt / sizeof(struct ptp_extts_event);
+
+ if (wait_event_interruptible(ptp->tsev_wq,
+ (qcnt = queue_cnt(&ptp->tsevq)))) {
+ mutex_unlock(&ptp->tsevq_mux);
+ return -ERESTARTSYS;
+ }
+
+ if (cnt > qcnt)
+ cnt = qcnt;
+
+ event = &queue->buf[ queue->head ];
+
+ if (copy_to_user(buf, event, cnt * sizeof(struct ptp_extts_event))) {
+ mutex_unlock(&ptp->tsevq_mux);
+ return -EFAULT;
+ }
+ queue->head = (queue->head + cnt) % PTP_MAX_TIMESTAMPS;
+
+ mutex_unlock(&ptp->tsevq_mux);
+
+ return cnt * sizeof(struct ptp_extts_event);
+}
+
+static int ptp_release(struct inode *inode, struct file *fp)
+{
+ struct ptp_clock *ptp;
+ struct itimerspec ts = {{0,0},{0,0}};
+ int i;
+
+ ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
+
+ for (i = 0; i < ptp->info->n_alarm; i++) {
+ if (ptp->alarm[i].pid) {
+ ptp->info->settimer(ptp->info->priv, i, 0, &ts);
+ put_pid(ptp->alarm[i].pid);
+ ptp->alarm[i].pid = NULL;
+ }
+ }
+ return 0;
+}
+
+static const struct file_operations ptp_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = ptp_ioctl,
+ .open = ptp_open,
+ .poll = ptp_poll,
+ .read = ptp_read,
+ .release = ptp_release,
+};
+
+/* sysfs */
+
+static ssize_t ptp_show_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ return sprintf(buf,
+ "maximum adjustment: %d\n"
+ "programmable alarms: %d\n"
+ "external timestamps: %d\n"
+ "periodic outputs: %d\n"
+ "has pps: %d\n"
+ "device index: %d\n"
+ ,ptp->info->max_adj
+ ,ptp->info->n_alarm
+ ,ptp->info->n_ext_ts
+ ,ptp->info->n_per_out
+ ,ptp->info->pps
+ ,ptp->index);
+}
+
+struct device_attribute ptp_attrs[] = {
+ __ATTR(capabilities, S_IRUGO, ptp_show_status, NULL),
+ __ATTR_NULL,
+};
+
+/* module operations */
+
+static void __exit ptp_exit(void)
+{
+ class_destroy(ptp_class);
+ unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
+}
+
+static int __init ptp_init(void)
+{
+ int err;
+
+ INIT_LIST_HEAD(&clocks.list);
+
+ ptp_class = class_create(THIS_MODULE, "ptp");
+ if (!ptp_class) {
+ printk(KERN_ERR "ptp: failed to allocate class\n");
+ return -ENOMEM;
+ }
+ ptp_class->dev_attrs = ptp_attrs;
+
+ err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
+ if (err < 0) {
+ printk(KERN_ERR "ptp: failed to allocate char device region\n");
+ goto no_region;
+ }
+
+ pr_info("PTP clock support registered\n");
+ return 0;
+
+no_region:
+ class_destroy(ptp_class);
+ 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 += pkt_sched.h
header-y += posix_types.h
header-y += ppdev.h
header-y += prctl.h
+header-y += ptp_clock.h
header-y += qnxtypes.h
header-y += qnx4_fs.h
header-y += 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
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _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 (1<<1)
+#define PTP_FALLING_EDGE (1<<2)
+
+enum ptp_request_types {
+ PTP_REQUEST_EXTTS,
+ PTP_REQUEST_PEROUT,
+ PTP_REQUEST_PPS,
+};
+
+struct ptp_clock_caps {
+ __s32 max_adj; /* Maximum frequency adjustment, parts per billon. */
+ int n_alarm; /* Number of programmable alarms. */
+ int n_ext_ts; /* Number of external time stamp channels. */
+ int n_per_out; /* Number of programmable periodic signals. */
+ int pps; /* Whether the clock supports a PPS callback. */
+};
+
+struct ptp_clock_timer {
+ int alarm_index; /* Which alarm to query or configure. */
+ int signum; /* Requested signal. */
+ int flags; /* Zero or TIMER_ABSTIME, see TIMER_SETTIME(2) */
+ struct itimerspec tsp; /* See TIMER_SETTIME(2) */
+};
+
+struct ptp_clock_request {
+ int type; /* One of the ptp_request_types enumeration values. */
+ int index; /* Which channel to configure. */
+ struct timespec ts; /* For period signals, the desired period. */
+ int flags; /* Bit field for PTP_ENABLE_FEATURE or other flags. */
+};
+
+struct ptp_extts_event {
+ int index;
+ struct timespec ts;
+};
+
+#define PTP_CLOCK_VERSION 0x00000001
+
+#define PTP_CLK_MAGIC '='
+
+#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)
+#define PTP_CLOCK_ADJFREQ _IO (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 _IOR (PTP_CLK_MAGIC, 6, struct ptp_clock_caps)
+#define PTP_CLOCK_GETTIMER _IOWR (PTP_CLK_MAGIC, 7, struct ptp_clock_timer)
+#define PTP_CLOCK_SETTIMER _IOW (PTP_CLK_MAGIC, 8, struct ptp_clock_timer)
+#define PTP_FEATURE_REQUEST _IOW (PTP_CLK_MAGIC, 9, struct ptp_clock_request)
+
+#endif
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
new file mode 100644
index 0000000..b1fb2a7
--- /dev/null
+++ b/include/linux/ptp_clock_kernel.h
@@ -0,0 +1,137 @@
+/*
+ * PTP 1588 clock support
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PTP_CLOCK_KERNEL_H_
+#define _PTP_CLOCK_KERNEL_H_
+
+#include <linux/ptp_clock.h>
+
+/**
+ * struct ptp_clock_info - decribes a PTP hardware clock
+ *
+ * @owner: The clock driver should set to THIS_MODULE.
+ * @name: A short name to identify the clock.
+ * @max_adj: The maximum possible frequency adjustment, in parts per billon.
+ * @n_alarm: The number of programmable alarms.
+ * @n_ext_ts: The number of external time stamp channels.
+ * @n_per_out: The number of programmable periodic signals.
+ * @pps: Indicates whether the clock supports a PPS callback.
+ * @priv: Passed to the clock operations, for the driver's private use.
+ *
+ * clock operations
+ *
+ * @adjfreq: Adjusts the frequency of the hardware clock.
+ * parameter delta: Desired period change in parts per billion.
+ *
+ * @adjtime: Shifts the time of the hardware clock.
+ * parameter ts: Desired change in seconds and nanoseconds.
+ *
+ * @gettime: Reads the current time from the hardware clock.
+ * parameter ts: Holds the result.
+ *
+ * @settime: Set the current time on the hardware clock.
+ * parameter ts: Time value to set.
+ *
+ * @gettimer: Reads the time remaining from the given timer.
+ * parameter index: Which alarm to query.
+ * parameter ts: Holds the result.
+ *
+ * @settimer: Arms the given timer for periodic or one shot operation.
+ * parameter index: Which alarm to set.
+ * parameter abs: TIMER_ABSTIME, or zero for relative timer.
+ * parameter ts: Alarm time and period to set.
+ *
+ * @enable: Request driver to enable or disable an ancillary feature.
+ * parameter request: Desired resource to enable or disable.
+ * parameter on: Caller passes one to enable or zero to disable.
+ *
+ * The callbacks must all return zero on success, non-zero otherwise.
+ */
+
+struct ptp_clock_info {
+ struct module *owner;
+ char name[16];
+ s32 max_adj;
+ int n_alarm;
+ int n_ext_ts;
+ int n_per_out;
+ int pps;
+ void *priv;
+ int (*adjfreq)(void *priv, s32 delta);
+ int (*adjtime)(void *priv, struct timespec *ts);
+ int (*gettime)(void *priv, struct timespec *ts);
+ int (*settime)(void *priv, struct timespec *ts);
+ int (*gettimer)(void *priv, int index, struct itimerspec *ts);
+ int (*settimer)(void *priv, int index, int abs, struct itimerspec *ts);
+ int (*enable)(void *priv, struct ptp_clock_request *request, int on);
+};
+
+struct ptp_clock;
+
+/**
+ * ptp_clock_register() - register a PTP hardware clock driver
+ *
+ * @info: Structure 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: The clock to remove from service.
+ */
+
+extern int ptp_clock_unregister(struct ptp_clock *ptp);
+
+
+enum ptp_clock_events {
+ PTP_CLOCK_ALARM,
+ PTP_CLOCK_EXTTS,
+ PTP_CLOCK_PPS,
+};
+
+/**
+ * struct ptp_clock_event - decribes a PTP hardware clock event
+ *
+ * @type: One of the ptp_clock_events enumeration values.
+ * @index: Identifies the source of the event.
+ * @timestamp: When the event occured.
+ */
+
+struct ptp_clock_event {
+ int type;
+ int index;
+ u64 timestamp;
+};
+
+/**
+ * ptp_clock_event() - notify the PTP layer about an event
+ *
+ * This function should only be called from interrupt context.
+ *
+ * @ptp: The clock obtained from ptp_clock_register().
+ * @event: Message structure describing the event.
+ */
+
+extern void ptp_clock_event(struct ptp_clock *ptp,
+ struct ptp_clock_event *event);
+
+#endif
--
1.6.3.3
^ permalink raw reply related
* [PATCH v3 0/3] ptp: IEEE 1588 clock support
From: Richard Cochran @ 2010-05-14 16:44 UTC (permalink / raw)
To: netdev; +Cc: devicetree-discuss, linuxppc-dev
Now and again there has been talk on this list of adding PTP support
into Linux. One part of the picture is already in place, the
SO_TIMESTAMPING API for hardware time stamping. This patch set offers
the missing second part needed for complete IEEE 1588 support.
The only feature still to be implemented is the hook into the PPS
subsystem, to synchronize the Linux clock to the PTP clock.
Enjoy,
Richard
* Patch ChangeLog
** v3
*** general
- Added documentation on writing clock drivers.
- Added the ioctls for the ancillary clock features.
- Changed wrong subsys_initcall() to module_init() in clock drivers.
- Removed the (too coarse) character device mutex.
- Setting the clock now requires CAP_SYS_TIME.
*** gianfar
- Added alarm feature.
- Added device tree node binding description.
- Added fine grain locking of the clock registers.
- Added the external time stamp feature.
- Added white space for better style.
- Coverted base+offset to structure pointers for register access.
- When removing the driver, we now disable all PTP functions.
** v2
- Changed clock list from a static array into a dynamic list. Also,
use a bitmap to manage the clock's minor numbers.
- Replaced character device semaphore with a mutex.
- Drop .ko from module names in Kbuild help.
- Replace deprecated unifdef-y with header-y for user space header file.
- Added links to both of the ptpd patches on sourceforge.
- Gianfar driver now gets parameters from device tree.
- Added API documentation to Documentation/ptp/ptp.txt
Richard Cochran (3):
ptp: Added a brand new class driver for ptp clocks.
ptp: Added a clock that uses the Linux system time.
ptp: Added a clock that uses the eTSEC found on the MPC85xx.
Documentation/powerpc/dts-bindings/fsl/tsec.txt | 56 +++
Documentation/ptp/ptp.txt | 95 ++++
Documentation/ptp/testptp.c | 245 +++++++++++
Documentation/ptp/testptp.mk | 33 ++
arch/powerpc/boot/dts/mpc8313erdb.dts | 14 +
arch/powerpc/boot/dts/p2020ds.dts | 14 +
arch/powerpc/boot/dts/p2020rdb.dts | 14 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/net/Makefile | 1 +
drivers/net/gianfar_ptp.c | 521 +++++++++++++++++++++++
drivers/net/gianfar_ptp_reg.h | 113 +++++
drivers/ptp/Kconfig | 51 +++
drivers/ptp/Makefile | 6 +
drivers/ptp/ptp_clock.c | 512 ++++++++++++++++++++++
drivers/ptp/ptp_linux.c | 136 ++++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 79 ++++
include/linux/ptp_clock_kernel.h | 137 ++++++
kernel/time/ntp.c | 2 +
20 files changed, 2033 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ptp/ptp.txt
create mode 100644 Documentation/ptp/testptp.c
create mode 100644 Documentation/ptp/testptp.mk
create mode 100644 drivers/net/gianfar_ptp.c
create mode 100644 drivers/net/gianfar_ptp_reg.h
create mode 100644 drivers/ptp/Kconfig
create mode 100644 drivers/ptp/Makefile
create mode 100644 drivers/ptp/ptp_clock.c
create mode 100644 drivers/ptp/ptp_linux.c
create mode 100644 include/linux/ptp_clock.h
create mode 100644 include/linux/ptp_clock_kernel.h
^ permalink raw reply
* Re: [PATCH] powerpc/fsl-booke: Move loadcam_entry back to asm code to fix SMP ftrace
From: Becky Bruce @ 2010-05-14 15:18 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <1273811137-30169-1-git-send-email-galak@kernel.crashing.org>
On May 13, 2010, at 11:25 PM, Kumar Gala wrote:
> When we build with ftrace enabled its possible that loadcam_entry
> would
> have used the stack pointer (even though the code doesn't need it).
> We
> call loadcam_entry in __secondary_start before the stack is setup. To
> ensure that loadcam_entry doesn't use the stack pointer the easiest
> solution is to just have it in asm code.
>
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Looks good on 8572 SMP - both cpus come up properly now with ftrace
enabled, and I am able to run the tracers.
Tested-by: Becky Bruce <beckyb@kernel.crashing.org>
> ---
> arch/powerpc/kernel/asm-offsets.c | 8 ++++++++
> arch/powerpc/mm/fsl_booke_mmu.c | 25 +++----------------------
> arch/powerpc/mm/mmu_decl.h | 10 +++++++++-
> arch/powerpc/mm/tlb_nohash_low.S | 28 ++++++++++++++++++++++++++++
> 4 files changed, 48 insertions(+), 23 deletions(-)
>
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/
> asm-offsets.c
> index c09138d..b894721 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -447,6 +447,14 @@ int main(void)
> DEFINE(PGD_T_LOG2, PGD_T_LOG2);
> DEFINE(PTE_T_LOG2, PTE_T_LOG2);
> #endif
> +#ifdef CONFIG_FSL_BOOKE
> + DEFINE(TLBCAM_SIZE, sizeof(struct tlbcam));
> + DEFINE(TLBCAM_MAS0, offsetof(struct tlbcam, MAS0));
> + DEFINE(TLBCAM_MAS1, offsetof(struct tlbcam, MAS1));
> + DEFINE(TLBCAM_MAS2, offsetof(struct tlbcam, MAS2));
> + DEFINE(TLBCAM_MAS3, offsetof(struct tlbcam, MAS3));
> + DEFINE(TLBCAM_MAS7, offsetof(struct tlbcam, MAS7));
> +#endif
>
> #ifdef CONFIG_KVM_EXIT_TIMING
> DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
> diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/
> fsl_booke_mmu.c
> index 1ed6b52..cdc7526 100644
> --- a/arch/powerpc/mm/fsl_booke_mmu.c
> +++ b/arch/powerpc/mm/fsl_booke_mmu.c
> @@ -2,7 +2,7 @@
> * Modifications by Kumar Gala (galak@kernel.crashing.org) to support
> * E500 Book E processors.
> *
> - * Copyright 2004 Freescale Semiconductor, Inc
> + * Copyright 2004,2010 Freescale Semiconductor, Inc.
> *
> * This file contains the routines for initializing the MMU
> * on the 4xx series of chips.
> @@ -56,19 +56,13 @@
>
> unsigned int tlbcam_index;
>
> -#define NUM_TLBCAMS (64)
>
> #if defined(CONFIG_LOWMEM_CAM_NUM_BOOL) && (CONFIG_LOWMEM_CAM_NUM >=
> NUM_TLBCAMS)
> #error "LOWMEM_CAM_NUM must be less than NUM_TLBCAMS"
> #endif
>
> -struct tlbcam {
> - u32 MAS0;
> - u32 MAS1;
> - unsigned long MAS2;
> - u32 MAS3;
> - u32 MAS7;
> -} TLBCAM[NUM_TLBCAMS];
> +#define NUM_TLBCAMS (64)
> +struct tlbcam TLBCAM[NUM_TLBCAMS];
>
> struct tlbcamrange {
> unsigned long start;
> @@ -109,19 +103,6 @@ unsigned long p_mapped_by_tlbcam(phys_addr_t pa)
> return 0;
> }
>
> -void loadcam_entry(int idx)
> -{
> - mtspr(SPRN_MAS0, TLBCAM[idx].MAS0);
> - mtspr(SPRN_MAS1, TLBCAM[idx].MAS1);
> - mtspr(SPRN_MAS2, TLBCAM[idx].MAS2);
> - mtspr(SPRN_MAS3, TLBCAM[idx].MAS3);
> -
> - if (mmu_has_feature(MMU_FTR_BIG_PHYS))
> - mtspr(SPRN_MAS7, TLBCAM[idx].MAS7);
> -
> - asm volatile("isync;tlbwe;isync" : : : "memory");
> -}
> -
> /*
> * Set up one of the I/D BAT (block address translation) register
> pairs.
> * The parameters are not checked; in particular size must be a power
> diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
> index d49a775..0591f25 100644
> --- a/arch/powerpc/mm/mmu_decl.h
> +++ b/arch/powerpc/mm/mmu_decl.h
> @@ -149,7 +149,15 @@ extern unsigned long mmu_mapin_ram(unsigned
> long top);
> extern void MMU_init_hw(void);
> extern unsigned long mmu_mapin_ram(unsigned long top);
> extern void adjust_total_lowmem(void);
> -
> +extern void loadcam_entry(unsigned int index);
> +
> +struct tlbcam {
> + u32 MAS0;
> + u32 MAS1;
> + unsigned long MAS2;
> + u32 MAS3;
> + u32 MAS7;
> +};
> #elif defined(CONFIG_PPC32)
> /* anything 32-bit except 4xx or 8xx */
> extern void MMU_init_hw(void);
> diff --git a/arch/powerpc/mm/tlb_nohash_low.S b/arch/powerpc/mm/
> tlb_nohash_low.S
> index bbdc5b5..8656ecf 100644
> --- a/arch/powerpc/mm/tlb_nohash_low.S
> +++ b/arch/powerpc/mm/tlb_nohash_low.S
> @@ -271,3 +271,31 @@ _GLOBAL(set_context)
> #else
> #error Unsupported processor type !
> #endif
> +
> +#if defined(CONFIG_FSL_BOOKE)
> +/*
> + * extern void loadcam_entry(unsigned int index)
> + *
> + * Load TLBCAM[index] entry in to the L2 CAM MMU
> + */
> +_GLOBAL(loadcam_entry)
> + LOAD_REG_ADDR(r4, TLBCAM)
> + mulli r5,r3,TLBCAM_SIZE
> + add r3,r5,r4
> + lwz r4,TLBCAM_MAS0(r3)
> + mtspr SPRN_MAS0,r4
> + lwz r4,TLBCAM_MAS1(r3)
> + mtspr SPRN_MAS1,r4
> + PPC_LL r4,TLBCAM_MAS2(r3)
> + mtspr SPRN_MAS2,r4
> + lwz r4,TLBCAM_MAS3(r3)
> + mtspr SPRN_MAS3,r4
> +BEGIN_MMU_FTR_SECTION
> + lwz r4,TLBCAM_MAS7(r3)
> + mtspr SPRN_MAS7,r4
> +END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS)
> + isync
> + tlbwe
> + isync
> + blr
> +#endif
> --
> 1.6.0.6
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* [PATCH] gianfar: Remove legacy PM callbacks
From: Anton Vorontsov @ 2010-05-14 14:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linuxppc-dev
These callbacks were needed because dev_pm_ops support for OF
platform devices was in the powerpc tree, and the patch that
added dev_pm_ops for gianfar driver was in the netdev tree. Now
that netdev and powerpc trees have merged into Linus' tree, we
can remove the legacy hooks.
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
---
drivers/net/gianfar.c | 14 --------------
1 files changed, 0 insertions(+), 14 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 5d3763f..fb23f04 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1288,21 +1288,9 @@ static struct dev_pm_ops gfar_pm_ops = {
#define GFAR_PM_OPS (&gfar_pm_ops)
-static int gfar_legacy_suspend(struct of_device *ofdev, pm_message_t state)
-{
- return gfar_suspend(&ofdev->dev);
-}
-
-static int gfar_legacy_resume(struct of_device *ofdev)
-{
- return gfar_resume(&ofdev->dev);
-}
-
#else
#define GFAR_PM_OPS NULL
-#define gfar_legacy_suspend NULL
-#define gfar_legacy_resume NULL
#endif
@@ -3055,8 +3043,6 @@ static struct of_platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
- .suspend = gfar_legacy_suspend,
- .resume = gfar_legacy_resume,
.driver.pm = GFAR_PM_OPS,
};
--
1.7.0.5
^ permalink raw reply related
* [PATCH v2] USB: FHCI: cq_get() should check kfifo_out()'s return value
From: Anton Vorontsov @ 2010-05-14 14:33 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linuxppc-dev, linux-usb
Since commit 7acd72eb85f1c7a15e8b5eb554994949241737f1 ("kfifo: rename
kfifo_put... into kfifo_in... and kfifo_get... into kfifo_out..."),
kfifo_out() is marked __must_check, and that causes gcc to produce
lots of warnings like this:
CC drivers/usb/host/fhci-mem.o
In file included from drivers/usb/host/fhci-hcd.c:34:
drivers/usb/host/fhci.h: In function 'cq_get':
drivers/usb/host/fhci.h:520: warning: ignoring return value of 'kfifo_out', declared with attribute warn_unused_result
...
This patch fixes the issue by properly checking the return value.
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
---
On Tue, May 04, 2010 at 12:17:19PM -0700, Greg KH wrote:
> On Sun, May 02, 2010 at 09:30:25PM +0400, Anton Vorontsov wrote:
> > Since commit 7acd72eb85f1c7a15e8b5eb554994949241737f1 ("kfifo: rename
> > kfifo_put... into kfifo_in... and kfifo_get... into kfifo_out..."),
> > kfifo_out() is marked __must_check, and that causes gcc to produce
> > lots of warnings like this:
> >
> > CC drivers/usb/host/fhci-mem.o
> > In file included from drivers/usb/host/fhci-hcd.c:34:
> > drivers/usb/host/fhci.h: In function 'cq_get':
> > drivers/usb/host/fhci.h:520: warning: ignoring return value of 'kfifo_out', declared with attribute warn_unused_result
> > ...
> >
> > Fix it with BUG_ON() check. We could just forcibly ignore return
> > value, but it's better to check if we really got a whole pointer.
>
> No, you don't want to crash the whole machine for something like this.
>
> Please just handle the error normally and pass it up to the upper levels
> if possible.
OK, here it is. Thanks!
drivers/usb/host/fhci.h | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
index 72dae1c..3b6e864 100644
--- a/drivers/usb/host/fhci.h
+++ b/drivers/usb/host/fhci.h
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/bug.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
@@ -515,9 +516,13 @@ static inline int cq_put(struct kfifo *kfifo, void *p)
static inline void *cq_get(struct kfifo *kfifo)
{
- void *p = NULL;
+ unsigned int sz;
+ void *p;
+
+ sz = kfifo_out(kfifo, (void *)&p, sizeof(p));
+ if (sz != sizeof(p))
+ return NULL;
- kfifo_out(kfifo, (void *)&p, sizeof(p));
return p;
}
--
1.7.0.5
^ permalink raw reply related
* [PATCH] fsl_pq_mdio: Fix mdiobus allocation handling
From: Anton Vorontsov @ 2010-05-14 14:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linuxppc-dev
The driver could return success code even if mdiobus_alloc() failed.
This patch fixes the issue.
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
---
drivers/net/fsl_pq_mdio.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index 3acac5f..ff028f5 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -277,15 +277,17 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
int tbiaddr = -1;
const u32 *addrp;
u64 addr = 0, size = 0;
- int err = 0;
+ int err;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
new_bus = mdiobus_alloc();
- if (NULL == new_bus)
+ if (!new_bus) {
+ err = -ENOMEM;
goto err_free_priv;
+ }
new_bus->name = "Freescale PowerQUICC MII Bus",
new_bus->read = &fsl_pq_mdio_read,
--
1.7.0.5
^ permalink raw reply related
* Re: [PATCH] media/IR: Add missing include file to rc-map.c
From: Stephen Rothwell @ 2010-05-14 12:53 UTC (permalink / raw)
To: Peter Hüwe
Cc: linux-mips, Paul Mundt, linux-sh, linux-kernel, David H?rdeman,
Mauro Carvalho Chehab, linuxppc-dev, linux-m68k, linux-next,
linux-media
In-Reply-To: <201005141326.52099.PeterHuewe@gmx.de>
[-- Attachment #1: Type: text/plain, Size: 630 bytes --]
Hi Peter,
On Fri, 14 May 2010 13:26:51 +0200 Peter Hüwe <PeterHuewe@gmx.de> wrote:
>
> From: Peter Huewe <peterhuewe@gmx.de>
>
> This patch adds a missing include linux/delay.h to prevent
> build failures[1-5]
>
> Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
> ---
> Forwarded to linux-next mailing list -
> breakage still exists in linux-next of 20100514 - please apply
This patch was included in the v4l-dvb tree (and thus linux-next) today
-see commit 1e19cb4e7d15d724cf2c6ae23f0b871c84a92790.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] media/IR: Add missing include file to rc-map.c
From: Peter Hüwe @ 2010-05-14 11:26 UTC (permalink / raw)
To: linux-next
Cc: linux-mips, linux-sh, linux-kernel, David H?rdeman,
Mauro Carvalho Chehab, linuxppc-dev, linux-m68k, Paul Mundt,
linux-media
In-Reply-To: <20100514060240.GD12002@linux-sh.org>
From: Peter Huewe <peterhuewe@gmx.de>
This patch adds a missing include linux/delay.h to prevent
build failures[1-5]
Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
---
Forwarded to linux-next mailing list -
breakage still exists in linux-next of 20100514 - please apply
KernelVersion: linux-next-20100505
References:
[1] http://kisskb.ellerman.id.au/kisskb/buildresult/2571452/
[2] http://kisskb.ellerman.id.au/kisskb/buildresult/2571188/
[3] http://kisskb.ellerman.id.au/kisskb/buildresult/2571479/
[4] http://kisskb.ellerman.id.au/kisskb/buildresult/2571429/
[5] http://kisskb.ellerman.id.au/kisskb/buildresult/2571432/
drivers/media/IR/rc-map.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/media/IR/rc-map.c b/drivers/media/IR/rc-map.c
index caf6a27..46a8f15 100644
--- a/drivers/media/IR/rc-map.c
+++ b/drivers/media/IR/rc-map.c
@@ -14,6 +14,7 @@
#include <media/ir-core.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
/* Used to handle IR raw handler extensions */
static LIST_HEAD(rc_map_list);
--
1.6.4.4
^ permalink raw reply related
* Re: [PATCH] media/IR: Add missing include file to rc-map.c
From: Peter Hüwe @ 2010-05-14 11:22 UTC (permalink / raw)
To: linux-next
Cc: linux-mips, linux-sh, linux-kernel, David H?rdeman,
Mauro Carvalho Chehab, linuxppc-dev, linux-m68k, Paul Mundt,
linux-media
In-Reply-To: <20100514060240.GD12002@linux-sh.org>
From: Peter Huewe <peterhuewe@gmx.de>
This patch adds a missing include linux/delay.h to prevent
build failures[1-5]
Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
---
Forwarded to linux-next mailing list -
breakage still exists in linux-next of 20100514 - please apply
KernelVersion: linux-next-20100505
References:
[1] http://kisskb.ellerman.id.au/kisskb/buildresult/2571452/
[2] http://kisskb.ellerman.id.au/kisskb/buildresult/2571188/
[3] http://kisskb.ellerman.id.au/kisskb/buildresult/2571479/
[4] http://kisskb.ellerman.id.au/kisskb/buildresult/2571429/
[5] http://kisskb.ellerman.id.au/kisskb/buildresult/2571432/
drivers/media/IR/rc-map.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/media/IR/rc-map.c b/drivers/media/IR/rc-map.c
index caf6a27..46a8f15 100644
--- a/drivers/media/IR/rc-map.c
+++ b/drivers/media/IR/rc-map.c
@@ -14,6 +14,7 @@
#include <media/ir-core.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
/* Used to handle IR raw handler extensions */
static LIST_HEAD(rc_map_list);
--
1.6.4.4
^ permalink raw reply related
* Re: [PATCH] powerpc: Add i8042 keyboard and mouse irq parsing
From: Martyn Welch @ 2010-05-14 10:39 UTC (permalink / raw)
To: grant.likely; +Cc: Dmitry Torokhov, linux-input, linuxppc-dev
In-Reply-To: <20100514102905.7807.27638.stgit@ES-J7S4D2J.amer.consind.ge.com>
Martyn Welch wrote:
> Currently the irqs for the i8042, which historically provides keyboard and
> mouse (aux) support, is hardwired in the driver rather than parsing the
> dts.
>
> In addition the interrupts are provided in the dts, but in a way that is
> not easily parsable using irq_of_parse_and_map().
>
> This patch modifies the powerpc legacy IO code to attempt to parse the
> device tree for this information, failing back to the hardcoded values if
> it fails. For this to succeed the interrupts for the keyboard and mouse
> ports need to be moved from the parent i8042 node to the individual port
> nodes.
>
> Signed-off-by: Martyn Welch <martyn.welch@ge.com>
> ---
>
> To get irq_of_parse_and_map() to successfully parse the interrupts, I had
> to do this to my device tree:
>
> @@ -120,16 +120,17 @@
> #address-cells = <1>;
> reg = <1 0x60 0x1
> 1 0x64 0x1>;
> - interrupts = <1 1 12 1>;
> interrupt-parent = <&lpc_pic>;
>
> keyboard@0 {
> reg = <0x0>;
> + interrupts = <1 1>;
> compatible = "pnpPNP,303";
> };
>
> mouse@1 {
> reg = <0x1>;
> + interrupts = <12 1>;
> compatible = "pnpPNP,f03";
> };
> };
>
> I'm not sure how to parse for the correct interrupt if I don't do this. I
> this is incorrect and someone could advise me on how the existing device
> tree layout can be properly parsed, I'll happily modify this patch.
>
>
Grant, any suggestions? (sorry, forgot to CC you)
Martyn
> arch/powerpc/kernel/setup-common.c | 34 +++++++++++++++++++++++++++++++---
> drivers/input/serio/i8042-io.h | 8 ++++++++
> 2 files changed, 39 insertions(+), 3 deletions(-)
>
> diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
> index 03dd6a2..9c4dc4c 100644
> --- a/arch/powerpc/kernel/setup-common.c
> +++ b/arch/powerpc/kernel/setup-common.c
> @@ -93,6 +93,10 @@ struct screen_info screen_info = {
> .orig_video_points = 16
> };
>
> +/* Variables required to store legacy IO irq routing */
> +int of_i8042_kbd_irq;
> +int of_i8042_aux_irq;
> +
> #ifdef __DO_IRQ_CANON
> /* XXX should go elsewhere eventually */
> int ppc_do_canonicalize_irqs;
> @@ -555,13 +559,37 @@ void probe_machine(void)
> /* Match a class of boards, not a specific device configuration. */
> int check_legacy_ioport(unsigned long base_port)
> {
> - struct device_node *parent, *np = NULL;
> + struct device_node *parent, *np = NULL, *np_aux = NULL;
> int ret = -ENODEV;
>
> switch(base_port) {
> case I8042_DATA_REG:
> - if (!(np = of_find_compatible_node(NULL, NULL, "pnpPNP,303")))
> - np = of_find_compatible_node(NULL, NULL, "pnpPNP,f03");
> + np = of_find_compatible_node(NULL, NULL, "pnpPNP,303");
> + if (np) {
> + /*
> + * Attempt to parse DTS for keyboard irq, fallback to
> + * standard.
> + */
> + of_i8042_kbd_irq = irq_of_parse_and_map(np, 0);
> + if (!of_i8042_kbd_irq)
> + of_i8042_kbd_irq = 1;
> + }
> +
> + np_aux = of_find_compatible_node(NULL, NULL, "pnpPNP,f03");
> + if (np_aux) {
> + if (!np) {
> + of_node_put(np);
> + np = np_aux;
> + }
> + /*
> + * Attempt to parse DTS for mouse (aux) irq, fallback to
> + * standard.
> + */
> + of_i8042_aux_irq = irq_of_parse_and_map(np_aux, 0);
> + if (!of_i8042_aux_irq)
> + of_i8042_aux_irq = 12;
> + }
> +
> if (np) {
> parent = of_get_parent(np);
> of_node_put(np);
> diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
> index 847f4aa..8fc8753 100644
> --- a/drivers/input/serio/i8042-io.h
> +++ b/drivers/input/serio/i8042-io.h
> @@ -19,6 +19,11 @@
> * IRQs.
> */
>
> +#if defined(CONFIG_PPC)
> +extern int of_i8042_kbd_irq;
> +extern int of_i8042_aux_irq;
> +#endif
> +
> #ifdef __alpha__
> # define I8042_KBD_IRQ 1
> # define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */
> @@ -27,6 +32,9 @@
> #include <asm/irq.h>
> #elif defined(CONFIG_SH_CAYMAN)
> #include <asm/irq.h>
> +#elif defined(CONFIG_PPC)
> +#define I8042_KBD_IRQ of_i8042_kbd_irq
> +#define I8042_AUX_IRQ of_i8042_aux_irq
> #else
> # define I8042_KBD_IRQ 1
> # define I8042_AUX_IRQ 12
>
>
> --
> Martyn Welch (Principal Software Engineer) | Registered in England and
> GE Intelligent Platforms | Wales (3828642) at 100
> T +44(0)127322748 | Barbirolli Square, Manchester,
> E martyn.welch@ge.com | M2 3AB VAT:GB 927559189
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
--
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms | Wales (3828642) at 100
T +44(0)127322748 | Barbirolli Square, Manchester,
E martyn.welch@ge.com | M2 3AB VAT:GB 927559189
^ permalink raw reply
* [PATCH] powerpc: Add i8042 keyboard and mouse irq parsing
From: Martyn Welch @ 2010-05-14 10:35 UTC (permalink / raw)
To: Dmitry Torokhov, benh; +Cc: linuxppc-dev, linux-input
Currently the irqs for the i8042, which historically provides keyboard and
mouse (aux) support, is hardwired in the driver rather than parsing the
dts.
In addition the interrupts are provided in the dts, but in a way that is
not easily parsable using irq_of_parse_and_map().
This patch modifies the powerpc legacy IO code to attempt to parse the
device tree for this information, failing back to the hardcoded values if
it fails. For this to succeed the interrupts for the keyboard and mouse
ports need to be moved from the parent i8042 node to the individual port
nodes.
Signed-off-by: Martyn Welch <martyn.welch@ge.com>
---
To get irq_of_parse_and_map() to successfully parse the interrupts, I had
to do this to my device tree:
@@ -120,16 +120,17 @@
#address-cells = <1>;
reg = <1 0x60 0x1
1 0x64 0x1>;
- interrupts = <1 1 12 1>;
interrupt-parent = <&lpc_pic>;
keyboard@0 {
reg = <0x0>;
+ interrupts = <1 1>;
compatible = "pnpPNP,303";
};
mouse@1 {
reg = <0x1>;
+ interrupts = <12 1>;
compatible = "pnpPNP,f03";
};
};
I'm not sure how to parse for the correct interrupt if I don't do this. I
this is incorrect and someone could advise me on how the existing device
tree layout can be properly parsed, I'll happily modify this patch.
arch/powerpc/kernel/setup-common.c | 34 +++++++++++++++++++++++++++++++---
drivers/input/serio/i8042-io.h | 8 ++++++++
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 03dd6a2..9c4dc4c 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -93,6 +93,10 @@ struct screen_info screen_info = {
.orig_video_points = 16
};
+/* Variables required to store legacy IO irq routing */
+int of_i8042_kbd_irq;
+int of_i8042_aux_irq;
+
#ifdef __DO_IRQ_CANON
/* XXX should go elsewhere eventually */
int ppc_do_canonicalize_irqs;
@@ -555,13 +559,37 @@ void probe_machine(void)
/* Match a class of boards, not a specific device configuration. */
int check_legacy_ioport(unsigned long base_port)
{
- struct device_node *parent, *np = NULL;
+ struct device_node *parent, *np = NULL, *np_aux = NULL;
int ret = -ENODEV;
switch(base_port) {
case I8042_DATA_REG:
- if (!(np = of_find_compatible_node(NULL, NULL, "pnpPNP,303")))
- np = of_find_compatible_node(NULL, NULL, "pnpPNP,f03");
+ np = of_find_compatible_node(NULL, NULL, "pnpPNP,303");
+ if (np) {
+ /*
+ * Attempt to parse DTS for keyboard irq, fallback to
+ * standard.
+ */
+ of_i8042_kbd_irq = irq_of_parse_and_map(np, 0);
+ if (!of_i8042_kbd_irq)
+ of_i8042_kbd_irq = 1;
+ }
+
+ np_aux = of_find_compatible_node(NULL, NULL, "pnpPNP,f03");
+ if (np_aux) {
+ if (!np) {
+ of_node_put(np);
+ np = np_aux;
+ }
+ /*
+ * Attempt to parse DTS for mouse (aux) irq, fallback to
+ * standard.
+ */
+ of_i8042_aux_irq = irq_of_parse_and_map(np_aux, 0);
+ if (!of_i8042_aux_irq)
+ of_i8042_aux_irq = 12;
+ }
+
if (np) {
parent = of_get_parent(np);
of_node_put(np);
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index 847f4aa..8fc8753 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -19,6 +19,11 @@
* IRQs.
*/
+#if defined(CONFIG_PPC)
+extern int of_i8042_kbd_irq;
+extern int of_i8042_aux_irq;
+#endif
+
#ifdef __alpha__
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */
@@ -27,6 +32,9 @@
#include <asm/irq.h>
#elif defined(CONFIG_SH_CAYMAN)
#include <asm/irq.h>
+#elif defined(CONFIG_PPC)
+#define I8042_KBD_IRQ of_i8042_kbd_irq
+#define I8042_AUX_IRQ of_i8042_aux_irq
#else
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ 12
--
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms | Wales (3828642) at 100
T +44(0)127322748 | Barbirolli Square, Manchester,
E martyn.welch@ge.com | M2 3AB VAT:GB 927559189
^ permalink raw reply related
* RE: [PATCH] pte_fsl_booke: fix instruction TLB error permission check
From: Benjamin Herrenschmidt @ 2010-05-14 9:25 UTC (permalink / raw)
To: Liu Dave-R63238; +Cc: linuxppc-dev, Jin Qing-B24347
In-Reply-To: <D7CCA83BB0796C49BC0BB53B6AB12089B975F5@zch01exm21.fsl.freescale.net>
On Fri, 2010-05-14 at 17:14 +0800, Liu Dave-R63238 wrote:
> > > Aaron pointed the issue on FSL BookE.
> > >
> > http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-January/079738.htm
> > > l
> >
> > Right, but I think 64-bit BookE has the same issue. We can
> > fix that later tho.
>
> Ben,
>
> We also find the _PAGE_USER is different between pte-book3e and old
> fsl-booke
> It is UR in old fsl-booke, and is _PAGE_BAP_UR | _PAGE_BAP_SR in
> pte-book3e,
> which will cause the ioremap_flags issue.
>
> Did you have a test your commit
> [ea3cc330ac0cd521ff07c7cd432a1848c19a7e92]?
I tested on some things :-) I keep asking Kumar to send me some e500
gear so I can actually test on it :-)
Cheers,
Ben.
^ permalink raw reply
* RE: [PATCH] pte_fsl_booke: fix instruction TLB error permission check
From: Liu Dave-R63238 @ 2010-05-14 9:14 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Jin Qing-B24347
In-Reply-To: <1273828131.21352.604.camel@pasglop>
> > Aaron pointed the issue on FSL BookE.
> >=20
> http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-January/079738.htm
> > l
>=20
> Right, but I think 64-bit BookE has the same issue. We can=20
> fix that later tho.
Ben,
We also find the _PAGE_USER is different between pte-book3e and old
fsl-booke
It is UR in old fsl-booke, and is _PAGE_BAP_UR | _PAGE_BAP_SR in
pte-book3e,
which will cause the ioremap_flags issue.
Did you have a test your commit
[ea3cc330ac0cd521ff07c7cd432a1848c19a7e92]?
Thanks, Dave
^ permalink raw reply
* RE: [PATCH] pte_fsl_booke: fix instruction TLB error permission check
From: Benjamin Herrenschmidt @ 2010-05-14 9:08 UTC (permalink / raw)
To: Liu Dave-R63238; +Cc: linuxppc-dev, Jin Qing-B24347
In-Reply-To: <D7CCA83BB0796C49BC0BB53B6AB12089B97531@zch01exm21.fsl.freescale.net>
On Fri, 2010-05-14 at 10:16 +0800, Liu Dave-R63238 wrote:
> > Not looking at the code right now ... but do we have the same
> > issue on 64e ?
>
> Aaron pointed the issue on FSL BookE.
> http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-January/079738.html
Right, but I think 64-bit BookE has the same issue. We can fix that
later tho.
Cheers
Ben
^ permalink raw reply
* [PATCH v2] trace, powerpc: Implement raw syscall tracepoints on PowerPC
From: Ian Munsie @ 2010-05-14 8:41 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Jason Baron, Steven Rostedt, Ingo Molnar,
Paul Mackerras, Ian Munsie, linuxppc-dev
In-Reply-To: <1273736594-19320-5-git-send-email-imunsie@au1.ibm.com>
From: Ian Munsie <imunsie@au.ibm.com>
This patch implements the raw syscall tracepoints on PowerPC and exports
them for ftrace syscalls to use.
To minimise reworking existing code, I slightly re-ordered the thread
info flags such that the new TIF_SYSCALL_TRACEPOINT bit would still fit
within the 16 bits of the andi. instruction's UI field. The instructions
in question are in /arch/powerpc/kernel/entry_{32,64}.S to and the
_TIF_SYSCALL_T_OR_A with the thread flags to see if system call tracing
is enabled.
In the case of 64bit PowerPC, arch_syscall_addr and
arch_syscall_match_sym_name are overridden to allow ftrace syscalls to
work given the unusual system call table structure and symbol names that
start with a period.
Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
Changes since v1: No functional changes, just removed the redundant conditional
export of sys_call_table on PPC32 vs PPC64, removed a cast and added an extra
comment to explain the arch_syscall_match_sym_name based on the feedback from
Michael Ellerman.
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/syscall.h | 5 +++++
arch/powerpc/include/asm/thread_info.h | 7 +++++--
arch/powerpc/kernel/Makefile | 1 +
arch/powerpc/kernel/ftrace.c | 19 +++++++++++++++++++
arch/powerpc/kernel/ptrace.c | 10 ++++++++++
6 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c4c4549..41e2f3e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -141,6 +141,7 @@ config PPC
select GENERIC_ATOMIC64 if PPC32
select HAVE_PERF_EVENTS
select HAVE_REGS_AND_STACK_ACCESS_API
+ select HAVE_SYSCALL_TRACEPOINTS
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 23913e9..b54b2ad 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -15,6 +15,11 @@
#include <linux/sched.h>
+/* ftrace syscalls requires exporting the sys_call_table */
+#ifdef CONFIG_FTRACE_SYSCALLS
+extern const unsigned long *sys_call_table;
+#endif /* CONFIG_FTRACE_SYSCALLS */
+
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index aa9d383..e7a8af2 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -110,7 +110,8 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_NOERROR 12 /* Force successful syscall return */
#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
#define TIF_FREEZE 14 /* Freezing for suspend */
-#define TIF_RUNLATCH 15 /* Is the runlatch enabled? */
+#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */
+#define TIF_RUNLATCH 16 /* Is the runlatch enabled? */
/* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
@@ -127,8 +128,10 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_NOERROR (1<<TIF_NOERROR)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE)
+#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
#define _TIF_RUNLATCH (1<<TIF_RUNLATCH)
-#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
+#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+ _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
_TIF_NOTIFY_RESUME)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 8773263..9c404bb 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -98,6 +98,7 @@ obj64-$(CONFIG_AUDIT) += compat_audit.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index ce1f3e4..f5fadbb 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -22,6 +22,7 @@
#include <asm/cacheflush.h>
#include <asm/code-patching.h>
#include <asm/ftrace.h>
+#include <asm/syscall.h>
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -600,3 +601,21 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
}
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64)
+unsigned long __init arch_syscall_addr(int nr)
+{
+ return sys_call_table[nr*2];
+}
+
+inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
+{
+ /*
+ * Compare the symbol name with the system call name. Skip the .sys or
+ * .SyS prefix from the symbol name and the sys prefix from the system
+ * call name and just match the rest. 32bit can use the generic
+ * function since their symbol names don't start with a period.
+ */
+ return (!strcmp(sym + 4, name + 3));
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 7a0c019..eb7eeb8 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -29,6 +29,7 @@
#include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
+#include <trace/syscall.h>
#ifdef CONFIG_PPC32
#include <linux/module.h>
#endif
@@ -38,6 +39,9 @@
#include <asm/pgtable.h>
#include <asm/system.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
/*
* The parameter save area on the stack is used to store arguments being passed
* to callee function and is located at fixed offset from stack pointer.
@@ -1615,6 +1619,9 @@ long do_syscall_trace_enter(struct pt_regs *regs)
*/
ret = -1L;
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_enter(regs, regs->gpr[0]);
+
if (unlikely(current->audit_context)) {
#ifdef CONFIG_PPC64
if (!test_thread_flag(TIF_32BIT))
@@ -1643,6 +1650,9 @@ void do_syscall_trace_leave(struct pt_regs *regs)
audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_exit(regs, regs->result);
+
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step);
--
1.7.1
^ permalink raw reply related
* Re: [Patch 2/2] PPC64-HWBKPT: Implement hw-breakpoints for PPC64
From: K.Prasad @ 2010-05-14 6:55 UTC (permalink / raw)
To: Paul Mackerras
Cc: Michael Neuling, Benjamin Herrenschmidt, shaggy,
Frederic Weisbecker, David Gibson, linuxppc-dev@ozlabs.org,
Alan Stern, Roland McGrath
In-Reply-To: <20100512063247.GC3548@drongo>
On Wed, May 12, 2010 at 04:32:47PM +1000, Paul Mackerras wrote:
> On Wed, May 05, 2010 at 02:03:03AM +0530, K.Prasad wrote:
>
> > It is true that the breakpoint exceptions will go amiss following the
> > alignment exception, and be restored when the thread single-steps due
> > to other requests causing undesirable effects. (Borrowing from some of
> > the discussions I had with BenH, earlier) There can be two ways of
> > changing the implementation to counter it:
> >
> > - To sense that the impending exception (alignment, page-fault,
> > single-step) is a successor to a hw-breakpoint exception (and that
> > restoration of debug register values is necessary), somewhere early in
> > exceptions-64s.S and jump to a common handler, say
> > do_single_step_dabr() which does a majority of
> > single_step_dabr_instruction().
> > - To modify emulate_single_step() to also do a notify_die(DIE_SSTEP,...)
> > in addition to its existing code. This would invoke
> > single_step_dabr_instruction() where the breakpoints can be restored.
>
> I thought you would change the explicit regs->msr modification in
> single_step_exception() to clear_single_step(), then just make
> emulate_single_step() call single_step_exception().
>
Okay. I will re-use single_step_exception() after modifications; it
appearsto have no in-kernel users for it. (single_step_exception() clears
MSR more than what clear_single_step() does, it shouldn't matter though).
> > I must admit that it is not clear to me when you say "doesn't return to
> > the instruction" and "instruction has been changed underneath". Are you
>
> Suppose the address at which the data breakpoint has been unmapped,
> and the process has a handler for the SIGSEGV signal. When we try to
> single-step the load or store, we will get a DSI (0x300) interrupt,
> call into do_page_fault, and end up sending the process a SIGSEGV.
> That will invoke the signal handler, which can then do anything it
> likes. It can do a blocking system call, it can longjmp() back into
> its main event, or it can return from the signal handler. Only in the
> last case will it retry the load or store, and then only if the signal
> handler hasn't modified the NIP value in the signal frame. That's
> what I mean by "doesn't return to the instruction".
>
At the outset, this seemed to be a scary thing to happen; but turns out
to be harmful only to the extent of generating a false hw-breakpoint
exception in certain cases. A case-by-case basis analysis reveals thus:
Consider an instruction stream i1, i2, i3, ... iN, where i1 has
finished execution and i2 is about to be executed but has generated a
DSI interrupt with the above-mentioned conditions i.e. DSISR indicates a
DABR match + Page-Table entry not found. Now according to do_hash_page
in exception-64s.S (as pasted below), do_page_fault() and do_dabr() are
invoked one after the other.
_STATIC(do_hash_page)
std r3,_DAR(r1)
std r4,_DSISR(r1)
andis. r0,r4,0xa410 /* weird error? */
bne- handle_page_fault /* if not, try to insert a HPTE */
andis. r0,r4,DSISR_DABRMATCH@h
bne- handle_dabr_fault
Thus, when control returns to user-space to instruction 'i2', the
hw_breakpoint_handler() has completed execution, and a SIGSEGV is pending
to be delivered and single-stepping enabled MSR_SE is set. Upon return to
user-space, the handler for SIGSEGV is executed and it may perform one of
the following (as you stated previously):
(a) Make a blocking syscall, eventually yielding the CPU to a new thread
(b) Jump to a different instruction in user-space, say iN, and not complete
the execution of instruction i2 at all.
(c) Return to instruction i2 and complete the execution.
In case of (a), the context-switches should not affect the ability to
single-step the instruction when the thread is eventually scheduled to
run. The thread, when scheduled onto the CPU will complete signal
handling, return to execute instruction i2, cause single-step exception,
restore breakpoints and run smoothly thereafter.
In case of (b), the new instruction iN is single-stepped, the breakpoint
values are restored and the hw-breakpoint exception callback is invoked
after iN is executed. The user of this breakpoint i.e. the caller of
register_user_hw_breakpoint() who had placed a breakpoint on addressed
accessed by instruction i2 will be confused to find that an unrelated
instruction (which may not be a load/store) has caused the breakpoint.
If so desired, we may adopt the 'trigger-before-execute' semantics for
user-space breakpoints wherein the hw-breakpoint callback (through
perf_bp_event()) is invoked in hw_breakpoint_handler() itself. This
would indicate to the user that the impending instruction causes a DABR
'hit' but it may or may not be executed due to the role of
signal-handler or due to self-modifying code (as mentioned below).
Kindly let me know what you think about it.
(c) is the normal execution path we desire. The instruction i2 will be
safely single-stepped and breakpoints are restored.
> The instruction could be changed underneath us if the program is
> multi-threaded and another thread writes another instruction to the
> instruction word where the load or store is. Or it could use mmap()
> to map some other page at the address of the load or store. Either
> way we could end up with a different instruction there.
>
If the instruction that originally caused the DABR exception is changed,
the new instruction in its place would still single-step to restore
breakpoint values. However the user of breakpoint interface will be
confused to find that the callback is invoked for an irrelevant
instruction.
It could be circumvented, to an extent, through the use of
trigger-before-execute semantics (as described before).
> > referring to the fact that the thread which generated breakpoints hits
> > could have moved out of the CPU due to a scheduler induced context
> > switch (which is an apparent cause for current->thread.last_hit_ubp to
> > turn stale) or is there a different source for such a change that I
> > don't realise?
> >
> > Given that 'last_hit_ubp' is safely ensconced inside 'thread_struct',
> > the ill-effects of a possible pre-emption during a page-fault will be
> > suitably handled i.e. the pending single-step exception will be
> > generated on the processor to which 'current' is migrated to, and the
> > breakpoint will be set on the new processor.
>
> If we do get a context switch, e.g. as a result of a page fault, and
> then switch back to the task, it looks to me like we will end up with
> MSR_SE and DABR both set. I don't suppose that will actually cause
> any real problem besides double-counting the hit.
>
Page fault exception will be handled before hw_breakpoint_handler(),
hence MSR_SE would not have been set if a context-switch happened in
pange-fault handling itself. I don't see a case where both MSR_SE and
DABR will be set together.
> > However, the possibility that current->thread.last_hit_ubp points to a
> > perf_event structure that is unregistered and freed does exist, and I
> > did not foresee the risk. An arch-specific function that hooks onto
> > release_bp_slot() would be required to perform the cleanup. I will
> > submit modify the patch to that effect. Thanks for pointing it out.
>
> Yes, I think we need that.
>
The same is implemented through arch_unregister_hw_breakpoint()
in version XVIII of the patch here: linuxppc-dev message-id:
20100512033315.GC6384@in.ibm.com.
> > In conjunction with what you've stated below, do you suggest that
> > emulate_step() be replaced with fix_alignment() which appears to be more
> > powerful at emulation (or carve out a helper function for fix_alignment()
> > that does only emulation and which can be invoked here)?
>
> Something like that eventually, but not for a first pass.
>
> Paul.
Thanks for the comments. Let me know if the analysis above is incorrect
or if I've failed to recognise any important issue that you pointed out.
I will send out a patch with changes to emulate_single_step() in the
next version of the patchset, if I don't hear any further comments.
Thanks,
K.Prasad
^ permalink raw reply
* Re: [PATCH] media/IR: Add missing include file to rc-map.c
From: Paul Mundt @ 2010-05-14 6:02 UTC (permalink / raw)
To: Peter H?we
Cc: linux-mips, linux-sh, linux-kernel, David H?rdeman,
Mauro Carvalho Chehab, linuxppc-dev, linux-m68k, linux-media
In-Reply-To: <201005112042.14889.PeterHuewe@gmx.de>
On Tue, May 11, 2010 at 08:42:14PM +0200, Peter H?we wrote:
> Am Mittwoch 05 Mai 2010 17:20:21 schrieb Peter H?we:
> > From: Peter Huewe <peterhuewe@gmx.de>
> >
> > This patch adds a missing include linux/delay.h to prevent
> > build failures[1-5]
> >
> > Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
> > ---
> Any updates on this patch?
> Issue still exists with today's linux-next tree
>
You might want to send this to the linux-next list at least. If the
people who introduced the breakage are unresponsive (as often tends to be
the case with -next) it's still worth getting trivial fixes rolled in for
the interim. This change doesn't exist outside of -next and whatever tree
introduced it, so there's not much else anyone can do about it at
present.
^ permalink raw reply
* [PATCH 2/2] powerpc, kdump: Fix race in kdump shutdown
From: Michael Neuling @ 2010-05-14 5:40 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, kexec, Anton Blanchard, jlarrew
In-Reply-To: <20100514035700.8B6EDCED6E@localhost.localdomain>
When we are crashing, the crashing/primary CPU IPIs the secondaries to
turn off IRQs, go into real mode and wait in kexec_wait. While this
is happening, the primary tears down all the MMU maps. Unfortunately
the primary doesn't check to make sure the secondaries have entered
real mode before doing this.
On PHYP machines, the secondaries can take a long time shutting down
the IRQ controller as RTAS calls are need. These RTAS calls need to
be serialised which resilts in the secondaries contending in
lock_rtas() and hence taking a long time to shut down.
We've hit this on large POWER7 machines, where some secondaries are
still waiting in lock_rtas(), when the primary tears down the HPTEs.
This patch makes sure all secondaries are in real mode before the
primary tears down the MMU. It uses the new kexec_state entry in the
paca. It times out if the secondaries don't reach real mode after
10sec.
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
arch/powerpc/kernel/crash.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
Index: linux-2.6-ozlabs/arch/powerpc/kernel/crash.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/crash.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/crash.c
@@ -162,6 +162,32 @@ static void crash_kexec_prepare_cpus(int
/* Leave the IPI callback set */
}
+/* wait for all the CPUs to hit real mode but timeout if they don't come in */
+static void crash_kexec_wait_realmode(int cpu)
+{
+ unsigned int msecs;
+ int i;
+
+ msecs = 10000;
+ for (i=0; i < NR_CPUS && msecs > 0; i++) {
+ if (i == cpu)
+ continue;
+
+ while (paca[i].kexec_state < KEXEC_STATE_REAL_MODE) {
+ barrier();
+ if (!cpu_possible(i)) {
+ break;
+ }
+ if (!cpu_online(i)) {
+ break;
+ }
+ msecs--;
+ mdelay(1);
+ }
+ }
+ mb();
+}
+
/*
* This function will be called by secondary cpus or by kexec cpu
* if soft-reset is activated to stop some CPUs.
@@ -412,6 +438,7 @@ void default_machine_crash_shutdown(stru
crash_kexec_prepare_cpus(crashing_cpu);
cpu_set(crashing_cpu, cpus_in_crash);
crash_kexec_stop_spus();
+ crash_kexec_wait_realmode(crashing_cpu);
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(1, 0);
}
^ permalink raw reply
* [PATCH 1/2] powerpc, kexec: Fix race in kexec shutdown
From: Michael Neuling @ 2010-05-14 5:40 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, kexec, Anton Blanchard, jlarrew
In-Reply-To: <20100514035700.8B6EDCED6E@localhost.localdomain>
In kexec_prepare_cpus, the primary CPU IPIs the secondary CPUs to
kexec_smp_down(). kexec_smp_down() calls kexec_smp_wait() which sets
the hw_cpu_id() to -1. The primary does this while leaving IRQs on
which means the primary can take a timer interrupt which can lead to
the IPIing one of the secondary CPUs (say, for a scheduler re-balance)
but since the secondary CPU now has a hw_cpu_id = -1, we IPI CPU
-1... Kaboom!
We are hitting this case regularly on POWER7 machines.
There is also a second race, where the primary will tear down the MMU
mappings before knowing the secondaries have entered real mode.
Also, the secondaries are clearing out any pending IPIs before
guaranteeing that no more will be received.
This changes kexec_prepare_cpus() so that we turn off IRQs in the
primary CPU much earlier. It adds a paca flag to say that the
secondaries have entered the kexec_smp_down() IPI and turned off IRQs,
rather than overloading hw_cpu_id with -1. This new paca flag is
again used to in indicate when the secondaries has entered real mode.
It also ensures that all CPUs have their IRQs off before we clear out
any pending IPI requests (in kexec_cpu_down()) to ensure there are no
trailing IPIs left unacknowledged.
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
Oops, missed quilt refresh in the last version...
---
arch/powerpc/include/asm/kexec.h | 4 ++
arch/powerpc/include/asm/paca.h | 1
arch/powerpc/kernel/asm-offsets.c | 1
arch/powerpc/kernel/machine_kexec_64.c | 48 +++++++++++++++++++++++----------
arch/powerpc/kernel/misc_64.S | 8 +++--
arch/powerpc/kernel/paca.c | 2 +
6 files changed, 47 insertions(+), 17 deletions(-)
Index: linux-2.6-ozlabs/arch/powerpc/include/asm/kexec.h
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/include/asm/kexec.h
+++ linux-2.6-ozlabs/arch/powerpc/include/asm/kexec.h
@@ -31,6 +31,10 @@
#define KEXEC_ARCH KEXEC_ARCH_PPC
#endif
+#define KEXEC_STATE_NONE 0
+#define KEXEC_STATE_IRQS_OFF 1
+#define KEXEC_STATE_REAL_MODE 2
+
#ifndef __ASSEMBLY__
#include <linux/cpumask.h>
#include <asm/reg.h>
Index: linux-2.6-ozlabs/arch/powerpc/include/asm/paca.h
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/include/asm/paca.h
+++ linux-2.6-ozlabs/arch/powerpc/include/asm/paca.h
@@ -82,6 +82,7 @@ struct paca_struct {
s16 hw_cpu_id; /* Physical processor number */
u8 cpu_start; /* At startup, processor spins until */
/* this becomes non-zero. */
+ u8 kexec_state; /* set when kexec down has irqs off */
#ifdef CONFIG_PPC_STD_MMU_64
struct slb_shadow *slb_shadow_ptr;
Index: linux-2.6-ozlabs/arch/powerpc/kernel/asm-offsets.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/asm-offsets.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/asm-offsets.c
@@ -184,6 +184,7 @@ int main(void)
#endif /* CONFIG_PPC_STD_MMU_64 */
DEFINE(PACAEMERGSP, offsetof(struct paca_struct, emergency_sp));
DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
+ DEFINE(PACAKEXECSTATE, offsetof(struct paca_struct, kexec_state));
DEFINE(PACA_STARTPURR, offsetof(struct paca_struct, startpurr));
DEFINE(PACA_STARTSPURR, offsetof(struct paca_struct, startspurr));
DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time));
Index: linux-2.6-ozlabs/arch/powerpc/kernel/machine_kexec_64.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/machine_kexec_64.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/machine_kexec_64.c
@@ -155,33 +155,38 @@ void kexec_copy_flush(struct kimage *ima
#ifdef CONFIG_SMP
-/* FIXME: we should schedule this function to be called on all cpus based
- * on calling the interrupts, but we would like to call it off irq level
- * so that the interrupt controller is clean.
- */
+static int kexec_all_irq_disabled = 0;
+
static void kexec_smp_down(void *arg)
{
+ local_irq_disable();
+ mb(); /* make sure our irqs are disabled before we say they are */
+ get_paca()->kexec_state = KEXEC_STATE_IRQS_OFF;
+ while(kexec_all_irq_disabled == 0)
+ cpu_relax();
+ mb(); /* make sure all irqs are disabled before this */
+ /*
+ * Now every CPU has IRQs off, we can clear out any pending
+ * IPIs and be sure that no more will come in after this.
+ */
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(0, 1);
- local_irq_disable();
kexec_smp_wait();
/* NOTREACHED */
}
-static void kexec_prepare_cpus(void)
+static void kexec_prepare_cpus_wait(int wait_state)
{
int my_cpu, i, notified=-1;
- smp_call_function(kexec_smp_down, NULL, /* wait */0);
my_cpu = get_cpu();
-
- /* check the others cpus are now down (via paca hw cpu id == -1) */
+ /* Make sure each CPU has atleast made it to the state we need */
for (i=0; i < NR_CPUS; i++) {
if (i == my_cpu)
continue;
- while (paca[i].hw_cpu_id != -1) {
+ while (paca[i].kexec_state < wait_state) {
barrier();
if (!cpu_possible(i)) {
printk("kexec: cpu %d hw_cpu_id %d is not"
@@ -201,20 +206,35 @@ static void kexec_prepare_cpus(void)
}
if (i != notified) {
printk( "kexec: waiting for cpu %d (physical"
- " %d) to go down\n",
- i, paca[i].hw_cpu_id);
+ " %d) to enter %i state\n",
+ i, paca[i].hw_cpu_id, wait_state);
notified = i;
}
}
}
+ mb();
+}
+
+static void kexec_prepare_cpus(void)
+{
+
+ smp_call_function(kexec_smp_down, NULL, /* wait */0);
+ local_irq_disable();
+ mb(); /* make sure IRQs are disabled before we say they are */
+ get_paca()->kexec_state = KEXEC_STATE_IRQS_OFF;
+
+ kexec_prepare_cpus_wait(KEXEC_STATE_IRQS_OFF);
+ /* we are sure every CPU has IRQs off at this point */
+ kexec_all_irq_disabled = 1;
/* after we tell the others to go down */
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(0, 0);
- put_cpu();
+ /* Before removing MMU mapings make sure all CPUs have entered real mode */
+ kexec_prepare_cpus_wait(KEXEC_STATE_REAL_MODE);
- local_irq_disable();
+ put_cpu();
}
#else /* ! SMP */
Index: linux-2.6-ozlabs/arch/powerpc/kernel/misc_64.S
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/misc_64.S
+++ linux-2.6-ozlabs/arch/powerpc/kernel/misc_64.S
@@ -24,6 +24,7 @@
#include <asm/asm-offsets.h>
#include <asm/cputable.h>
#include <asm/thread_info.h>
+#include <asm/kexec.h>
.text
@@ -471,6 +472,10 @@ _GLOBAL(kexec_wait)
1: mflr r5
addi r5,r5,kexec_flag-1b
+ li r4,KEXEC_STATE_REAL_MODE
+ stb r4,PACAKEXECSTATE(r13)
+ SYNC
+
99: HMT_LOW
#ifdef CONFIG_KEXEC /* use no memory without kexec */
lwz r4,0(r5)
@@ -494,14 +499,11 @@ kexec_flag:
* note: this is a terminal routine, it does not save lr
*
* get phys id from paca
- * set paca id to -1 to say we got here
* switch to real mode
* join other cpus in kexec_wait(phys_id)
*/
_GLOBAL(kexec_smp_wait)
lhz r3,PACAHWCPUID(r13)
- li r4,-1
- sth r4,PACAHWCPUID(r13) /* let others know we left */
bl real_mode
b .kexec_wait
Index: linux-2.6-ozlabs/arch/powerpc/kernel/paca.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/paca.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/paca.c
@@ -18,6 +18,7 @@
#include <asm/pgtable.h>
#include <asm/iseries/lpar_map.h>
#include <asm/iseries/hv_types.h>
+#include <asm/kexec.h>
/* This symbol is provided by the linker - let it fill in the paca
* field correctly */
@@ -97,6 +98,7 @@ void __init initialise_paca(struct paca_
new_paca->kernelbase = (unsigned long) _stext;
new_paca->kernel_msr = MSR_KERNEL;
new_paca->hw_cpu_id = 0xffff;
+ new_paca->kexec_state = KEXEC_STATE_NONE;
new_paca->__current = &init_task;
#ifdef CONFIG_PPC_STD_MMU_64
new_paca->slb_shadow_ptr = &slb_shadow[cpu];
^ 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