LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: How to use mpc8xxx_gpio.c device driver
From: Ravi Gupta @ 2010-08-13 10:01 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linuxppc-dev, linuxppc-dev
In-Reply-To: <20100812153618.GA7498@ovro.caltech.edu>

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

> Looking at the device tree for this board, it appears U-Boot remaps the
> IMMR registers to 0xe0000000. They are no longer accessible at
> 0xff400000.
>
> I would recommend studying arch/powerpc/boot/dts/mpc8377_rdb.dts in the
> Linux source code. That describes the device layout on your board after
> U-Boot has run.
>
> A wonderful tool for testing devices from userspace is "busybox devmem".
> It allows you to poke any physical address with any value. The output of
> "busybox devmem --help" should get you started. As a quick example,
> "busybox devmem 0xe0000c00 w 0x1" will write the 32-bit value 0x1 to
> address 0xe0000c00.
>
> I would also recommend using the built-in Linux GPIO API. It works, you
> just need to figure out how to use it. It will be much easier to get
> your code upstream if you use the provided APIs.
>
> The Documentation/gpio.txt file should help you in understanding the
> in-kernel Linux GPIO API. I'm afraid I don't have much experience other
> than accessing it via sysfs from userspace.
>
> Ira
>

Hi Ira,

Thanks for another great reply. Now I can also access gpio memory map
registers.

Thanks and Regards,
Ravi Gupta

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

^ permalink raw reply

* Re: How to use mpc8xxx_gpio.c device driver
From: Anton Vorontsov @ 2010-08-13 13:16 UTC (permalink / raw)
  To: Ravi Gupta; +Cc: linuxppc-dev, MJ embd, linuxppc-dev, Ira W. Snyder
In-Reply-To: <AANLkTi=H1sKaGL8cHm9LAZm1tzm88dvkReXk12hmkWLj@mail.gmail.com>

Hi,

On Fri, Aug 13, 2010 at 03:29:11PM +0530, Ravi Gupta wrote:
[...]
> Thanks for the reply.
> I had added the entries for gpio pin 9 for both controllers(I was not sure
> with controller's pin is connected to LED, but now I know it is pin no. 233
> i.e 224+9) in the mpc8377_rdb.dts file. Below is a portion of my dts file, I
> have attached the complete dts file as attachment.
> 
> immr@e0000000 {
[...]
>    * led@0 {
>         compatible = "gpio-leds";
>         label = "hdd";
>         gpios = <&gpio1 9 0>;
>     };

What kernel version you look at? Please see the latest kernel,
it has MCU GPIO LED nodes already, and you can just add some
additional nodes.

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/powerpc/boot/dts/mpc8377_rdb.dts#l490

[...]
> Also I have enabled drivers/leds/leds-gpio.c in my kernel. To test whether
> the leds entires in dts file get attached to leds-gpio driver, I added
> printks in the probe function of the driver.
> 
> static int gpio_led_probe(struct platform_device *pdev)
> {
>   struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
>   struct gpio_led *cur_led;
>   struct gpio_led_data *leds_data, *led_dat;
>   int i, ret = 0;
> 
>   *printk(KERN_INFO "led: inside gpio_led_probe.\n");*

You have put the printk into the wrong function. It should
have been of_gpio_leds_probe():

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/leds/leds-gpio.c#l227

If you don't have that function then you use too old kernel.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* [PATCH 0/9] RapidIO: Set of patches to add Gen2 switches
From: Alexandre Bounine @ 2010-08-13 15:17 UTC (permalink / raw)
  To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll

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

^ permalink raw reply

* [PATCH 1/9] RapidIO: fix RapidIO sysfs hierarchy
From: Alexandre Bounine @ 2010-08-13 15:17 UTC (permalink / raw)
  To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1281712686-31308-1-git-send-email-alexandre.bounine@idt.com>

Makes RapidIO devices appear in /sys/devices/rapidio directory instead of top
of /sys/devices directory.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio-driver.c |    2 +-
 drivers/rapidio/rio-scan.c   |    1 +
 include/linux/rio.h          |    1 +
 3 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c
index 3222fa3..0f4a53b 100644
--- a/drivers/rapidio/rio-driver.c
+++ b/drivers/rapidio/rio-driver.c
@@ -192,7 +192,7 @@ static int rio_match_bus(struct device *dev, struct device_driver *drv)
       out:return 0;
 }
 
-static struct device rio_bus = {
+struct device rio_bus = {
 	.init_name = "rapidio",
 };
 
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 8070e07..1123be8 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -478,6 +478,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 	}
 
 	rdev->dev.bus = &rio_bus_type;
+	rdev->dev.parent = &rio_bus;
 
 	device_initialize(&rdev->dev);
 	rdev->dev.release = rio_release_dev;
diff --git a/include/linux/rio.h b/include/linux/rio.h
index bd6eb0e..84c9f8c 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -67,6 +67,7 @@
 #define RIO_PW_MSG_SIZE		64
 
 extern struct bus_type rio_bus_type;
+extern struct device rio_bus;
 extern struct list_head rio_devices;	/* list of all devices */
 
 struct rio_mport;
-- 
1.7.0.5

^ permalink raw reply related

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

- Rearranged RIO port-write interrupt handling to perform message buffering
as soon as possible.
- Modified to disable port-write controller when clearing Transaction Error (TE)
bit.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 arch/powerpc/sysdev/fsl_rio.c |   67 ++++++++++++++++++++++------------------
 1 files changed, 37 insertions(+), 30 deletions(-)

diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index cd71dc1..708d94e 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -1065,18 +1065,12 @@ fsl_rio_port_write_handler(int irq, void *dev_instance)
 	struct rio_priv *priv = port->priv;
 	u32 epwisr, tmp;
 
-	ipwmr = in_be32(&priv->msg_regs->pwmr);
-	ipwsr = in_be32(&priv->msg_regs->pwsr);
-
 	epwisr = in_be32(priv->regs_win + RIO_EPWISR);
-	if (epwisr & 0x80000000) {
-		tmp = in_be32(priv->regs_win + RIO_LTLEDCSR);
-		pr_info("RIO_LTLEDCSR = 0x%x\n", tmp);
-		out_be32(priv->regs_win + RIO_LTLEDCSR, 0);
-	}
-
 	if (!(epwisr & 0x00000001))
-		return IRQ_HANDLED;
+		goto pw_done;
+
+	ipwmr = in_be32(&priv->msg_regs->pwmr);
+	ipwsr = in_be32(&priv->msg_regs->pwsr);
 
 #ifdef DEBUG_PW
 	pr_debug("PW Int->IPWMR: 0x%08x IPWSR: 0x%08x (", ipwmr, ipwsr);
@@ -1092,22 +1086,8 @@ fsl_rio_port_write_handler(int irq, void *dev_instance)
 		pr_debug(" PWB");
 	pr_debug(" )\n");
 #endif
-	out_be32(&priv->msg_regs->pwsr,
-		 ipwsr & (RIO_IPWSR_TE | RIO_IPWSR_QFI | RIO_IPWSR_PWD));
-
-	if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) {
-		priv->port_write_msg.err_count++;
-		pr_info("RIO: Port-Write Transaction Err (%d)\n",
-			 priv->port_write_msg.err_count);
-	}
-	if (ipwsr & RIO_IPWSR_PWD) {
-		priv->port_write_msg.discard_count++;
-		pr_info("RIO: Port Discarded Port-Write Msg(s) (%d)\n",
-			 priv->port_write_msg.discard_count);
-	}
-
 	/* Schedule deferred processing if PW was received */
-	if (ipwsr & RIO_IPWSR_QFI) {
+	if ((ipwmr & RIO_IPWMR_QFIE) && (ipwsr & RIO_IPWSR_QFI)) {
 		/* Save PW message (if there is room in FIFO),
 		 * otherwise discard it.
 		 */
@@ -1117,16 +1097,43 @@ fsl_rio_port_write_handler(int irq, void *dev_instance)
 				 RIO_PW_MSG_SIZE);
 		} else {
 			priv->port_write_msg.discard_count++;
-			pr_info("RIO: ISR Discarded Port-Write Msg(s) (%d)\n",
+			pr_debug("RIO: ISR Discarded Port-Write Msg(s) (%d)\n",
 				 priv->port_write_msg.discard_count);
 		}
+		/* Clear interrupt and issue Clear Queue command. This allows
+		 * another port-write to be received.
+		 */
+		out_be32(&priv->msg_regs->pwsr,	RIO_IPWSR_QFI);
+		out_be32(&priv->msg_regs->pwmr, ipwmr | RIO_IPWMR_CQ);
+
 		schedule_work(&priv->pw_work);
 	}
 
-	/* Issue Clear Queue command. This allows another
-	 * port-write to be received.
-	 */
-	out_be32(&priv->msg_regs->pwmr, ipwmr | RIO_IPWMR_CQ);
+	if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) {
+		priv->port_write_msg.err_count++;
+		pr_debug("RIO: Port-Write Transaction Err (%d)\n",
+			 priv->port_write_msg.err_count);
+		/* Clear Transaction Error: port-write controller should be
+		 * disabled when clearing this error
+		 */
+		out_be32(&priv->msg_regs->pwmr, ipwmr & ~RIO_IPWMR_PWE);
+		out_be32(&priv->msg_regs->pwsr,	RIO_IPWSR_TE);
+		out_be32(&priv->msg_regs->pwmr, ipwmr);
+	}
+
+	if (ipwsr & RIO_IPWSR_PWD) {
+		priv->port_write_msg.discard_count++;
+		pr_debug("RIO: Port Discarded Port-Write Msg(s) (%d)\n",
+			 priv->port_write_msg.discard_count);
+		out_be32(&priv->msg_regs->pwsr, RIO_IPWSR_PWD);
+	}
+
+pw_done:
+	if (epwisr & 0x80000000) {
+		tmp = in_be32(priv->regs_win + RIO_LTLEDCSR);
+		pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+		out_be32(priv->regs_win + RIO_LTLEDCSR, 0);
+	}
 
 	return IRQ_HANDLED;
 }
-- 
1.7.0.5

^ permalink raw reply related

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

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

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio-scan.c |   38 ++++++++------------------------------
 include/linux/rio.h        |    4 ++--
 2 files changed, 10 insertions(+), 32 deletions(-)

diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 1123be8..efe3519 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -389,6 +389,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 	int ret = 0;
 	struct rio_dev *rdev;
 	struct rio_switch *rswitch = NULL;
+	u32 swpinfo;
 	int result, rdid;
 
 	rdev = kzalloc(sizeof(struct rio_dev), GFP_KERNEL);
@@ -440,7 +441,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 	/* If a PE has both switch and other functions, show it as a switch */
 	if (rio_is_switch(rdev)) {
 		rio_mport_read_config_32(port, destid, hopcount,
-					 RIO_SWP_INFO_CAR, &rdev->swpinfo);
+					 RIO_SWP_INFO_CAR, &swpinfo);
 		rswitch = kzalloc(sizeof(struct rio_switch), GFP_KERNEL);
 		if (!rswitch)
 			goto cleanup;
@@ -448,6 +449,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 		rswitch->hopcount = hopcount;
 		rswitch->destid = destid;
 		rswitch->port_ok = 0;
+		rswitch->inport = (u8)(swpinfo & RIO_SWP_INFO_PORT_NUM_MASK);
 		rswitch->route_table = kzalloc(sizeof(u8)*
 					RIO_MAX_ROUTE_ENTRIES(port->sys_size),
 					GFP_KERNEL);
@@ -719,25 +721,6 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount)
 }
 
 /**
- * rio_get_swpinfo_inport- Gets the ingress port number
- * @mport: Master port to send transaction
- * @destid: Destination ID associated with the switch
- * @hopcount: Number of hops to the device
- *
- * Returns port number being used to access the switch device.
- */
-static u8
-rio_get_swpinfo_inport(struct rio_mport *mport, u16 destid, u8 hopcount)
-{
-	u32 result;
-
-	rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR,
-				 &result);
-
-	return (u8) (result & 0xff);
-}
-
-/**
  * rio_get_swpinfo_tports- Gets total number of ports on the switch
  * @mport: Master port to send transaction
  * @destid: Destination ID associated with the switch
@@ -834,8 +817,7 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 
 	if (rio_is_switch(rdev)) {
 		next_switchid++;
-		sw_inport = rio_get_swpinfo_inport(port,
-				RIO_ANY_DESTID(port->sys_size), hopcount);
+		sw_inport = rdev->rswitch->inport;
 		rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
 				    port->host_deviceid, sw_inport, 0);
 		rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
@@ -989,8 +971,7 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
 		    "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
 		    rio_name(rdev), rdev->vid, rdev->did, num_ports);
 		for (port_num = 0; port_num < num_ports; port_num++) {
-			if (rio_get_swpinfo_inport(port, destid, hopcount) ==
-			    port_num)
+			if (rdev->rswitch->inport == port_num)
 				continue;
 
 			if (rio_sport_is_active
@@ -1092,7 +1073,6 @@ static void rio_update_route_tables(struct rio_mport *port)
 {
 	struct rio_dev *rdev;
 	struct rio_switch *rswitch;
-	u8 sport;
 	u16 destid;
 
 	list_for_each_entry(rdev, &rio_devices, global_list) {
@@ -1109,14 +1089,12 @@ static void rio_update_route_tables(struct rio_mport *port)
 				if (rswitch->destid == destid)
 					continue;
 
-				sport = rio_get_swpinfo_inport(port,
-						rswitch->destid, rswitch->hopcount);
-
 				if (rswitch->add_entry)	{
 					rio_route_add_entry(port, rswitch,
 						RIO_GLOBAL_TABLE, destid,
-						sport, 0);
-					rswitch->route_table[destid] = sport;
+						rswitch->inport, 0);
+					rswitch->route_table[destid] =
+								rswitch->inport;
 				}
 			}
 		}
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 84c9f8c..718075a 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -86,7 +86,6 @@ union rio_pw_msg;
  * @asm_rev: Assembly revision
  * @efptr: Extended feature pointer
  * @pef: Processing element features
- * @swpinfo: Switch port info
  * @src_ops: Source operation capabilities
  * @dst_ops: Destination operation capabilities
  * @comp_tag: RIO component tag
@@ -112,7 +111,6 @@ struct rio_dev {
 	u16 asm_rev;
 	u16 efptr;
 	u32 pef;
-	u32 swpinfo;		/* Only used for switches */
 	u32 src_ops;
 	u32 dst_ops;
 	u32 comp_tag;
@@ -222,6 +220,7 @@ struct rio_net {
  * @switchid: Switch ID that is unique across a network
  * @hopcount: Hopcount to this switch
  * @destid: Associated destid in the path
+ * @inport: Switch ingress port number
  * @route_table: Copy of switch routing table
  * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
  * @add_entry: Callback for switch-specific route add function
@@ -237,6 +236,7 @@ struct rio_switch {
 	u16 switchid;
 	u16 hopcount;
 	u16 destid;
+	u8  inport;
 	u8 *route_table;
 	u32 port_ok;
 	int (*add_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
-- 
1.7.0.5

^ permalink raw reply related

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

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

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio-scan.c |   55 +++++++++++++++++--------------------------
 include/linux/rio.h        |    6 ++++
 2 files changed, 28 insertions(+), 33 deletions(-)

diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index efe3519..5dc33d1 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -442,7 +442,10 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 	if (rio_is_switch(rdev)) {
 		rio_mport_read_config_32(port, destid, hopcount,
 					 RIO_SWP_INFO_CAR, &swpinfo);
-		rswitch = kzalloc(sizeof(struct rio_switch), GFP_KERNEL);
+		rswitch = kzalloc(sizeof(*rswitch) +
+				  RIO_GET_TOTAL_PORTS(swpinfo) *
+				  sizeof(rswitch->nextdev[0]),
+				  GFP_KERNEL);
 		if (!rswitch)
 			goto cleanup;
 		rswitch->switchid = next_switchid;
@@ -450,6 +453,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 		rswitch->destid = destid;
 		rswitch->port_ok = 0;
 		rswitch->inport = (u8)(swpinfo & RIO_SWP_INFO_PORT_NUM_MASK);
+		rswitch->nports = (u8)RIO_GET_TOTAL_PORTS(swpinfo);
 		rswitch->route_table = kzalloc(sizeof(u8)*
 					RIO_MAX_ROUTE_ENTRIES(port->sys_size),
 					GFP_KERNEL);
@@ -721,25 +725,6 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount)
 }
 
 /**
- * rio_get_swpinfo_tports- Gets total number of ports on the switch
- * @mport: Master port to send transaction
- * @destid: Destination ID associated with the switch
- * @hopcount: Number of hops to the device
- *
- * Returns total numbers of ports implemented by the switch device.
- */
-static u8 rio_get_swpinfo_tports(struct rio_mport *mport, u16 destid,
-				 u8 hopcount)
-{
-	u32 result;
-
-	rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR,
-				 &result);
-
-	return RIO_GET_TOTAL_PORTS(result);
-}
-
-/**
  * rio_net_add_mport- Add a master port to a RIO network
  * @net: RIO network
  * @port: Master port to add
@@ -759,15 +744,16 @@ static void rio_net_add_mport(struct rio_net *net, struct rio_mport *port)
  * @net: RIO network being enumerated
  * @port: Master port to send transactions
  * @hopcount: Number of hops into the network
+ * @prev: Previous RIO device connected to the enumerated one
+ * @prev_port: Port on previous RIO device
  *
  * Recursively enumerates a RIO network.  Transactions are sent via the
  * master port passed in @port.
  */
 static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
-			 u8 hopcount)
+			 u8 hopcount, struct rio_dev *prev, int prev_port)
 {
 	int port_num;
-	int num_ports;
 	int cur_destid;
 	int sw_destid;
 	int sw_inport;
@@ -812,6 +798,9 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 	if (rdev) {
 		/* Add device to the global and bus/net specific list. */
 		list_add_tail(&rdev->net_list, &net->devices);
+		rdev->prev = prev;
+		if (prev && rio_is_switch(prev))
+			prev->rswitch->nextdev[prev_port] = rdev;
 	} else
 		return -1;
 
@@ -830,14 +819,13 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 			rdev->rswitch->route_table[destid] = sw_inport;
 		}
 
-		num_ports =
-		    rio_get_swpinfo_tports(port, RIO_ANY_DESTID(port->sys_size),
-						hopcount);
 		pr_debug(
 		    "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
-		    rio_name(rdev), rdev->vid, rdev->did, num_ports);
+		    rio_name(rdev), rdev->vid, rdev->did,
+		    rdev->rswitch->nports);
 		sw_destid = next_destid;
-		for (port_num = 0; port_num < num_ports; port_num++) {
+		for (port_num = 0;
+		     port_num < rdev->rswitch->nports; port_num++) {
 			/*Enable Input Output Port (transmitter reviever)*/
 			rio_enable_rx_tx_port(port, 0,
 					      RIO_ANY_DESTID(port->sys_size),
@@ -862,7 +850,8 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 						RIO_ANY_DESTID(port->sys_size),
 						port_num, 0);
 
-				if (rio_enum_peer(net, port, hopcount + 1) < 0)
+				if (rio_enum_peer(net, port, hopcount + 1,
+						  rdev, port_num) < 0)
 					return -1;
 
 				/* Update routing tables */
@@ -949,7 +938,6 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
 	      u8 hopcount)
 {
 	u8 port_num, route_port;
-	int num_ports;
 	struct rio_dev *rdev;
 	u16 ndestid;
 
@@ -966,11 +954,12 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
 		/* Associated destid is how we accessed this switch */
 		rdev->rswitch->destid = destid;
 
-		num_ports = rio_get_swpinfo_tports(port, destid, hopcount);
 		pr_debug(
 		    "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
-		    rio_name(rdev), rdev->vid, rdev->did, num_ports);
-		for (port_num = 0; port_num < num_ports; port_num++) {
+		    rio_name(rdev), rdev->vid, rdev->did,
+		    rdev->rswitch->nports);
+		for (port_num = 0;
+		     port_num < rdev->rswitch->nports; port_num++) {
 			if (rdev->rswitch->inport == port_num)
 				continue;
 
@@ -1163,7 +1152,7 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
 		/* Enable Input Output Port (transmitter reviever) */
 		rio_enable_rx_tx_port(mport, 1, 0, 0, 0);
 
-		if (rio_enum_peer(net, mport, 0) < 0) {
+		if (rio_enum_peer(net, mport, 0, NULL, 0) < 0) {
 			/* A higher priority host won enumeration, bail. */
 			printk(KERN_INFO
 			       "RIO: master port %d device has lost enumeration to a remote host\n",
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 718075a..754895c 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -98,6 +98,7 @@ union rio_pw_msg;
  * @riores: RIO resources this device owns
  * @pwcback: port-write callback function for this device
  * @destid: Network destination ID
+ * @prev: Previous RIO device connected to the current one
  */
 struct rio_dev {
 	struct list_head global_list;	/* node in list of all RIO devices */
@@ -123,6 +124,7 @@ struct rio_dev {
 	struct resource riores[RIO_MAX_DEV_RESOURCES];
 	int (*pwcback) (struct rio_dev *rdev, union rio_pw_msg *msg, int step);
 	u16 destid;
+	struct rio_dev *prev;
 };
 
 #define rio_dev_g(n) list_entry(n, struct rio_dev, global_list)
@@ -221,6 +223,7 @@ struct rio_net {
  * @hopcount: Hopcount to this switch
  * @destid: Associated destid in the path
  * @inport: Switch ingress port number
+ * @nports: Total number of ports in the switch
  * @route_table: Copy of switch routing table
  * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
  * @add_entry: Callback for switch-specific route add function
@@ -230,6 +233,7 @@ struct rio_net {
  * @get_domain: Callback for switch-specific domain get function
  * @em_init: Callback for switch-specific error management initialization function
  * @em_handle: Callback for switch-specific error management handler function
+ * @nextdev: Array of per-port pointers to the next attached device
  */
 struct rio_switch {
 	struct list_head node;
@@ -237,6 +241,7 @@ struct rio_switch {
 	u16 hopcount;
 	u16 destid;
 	u8  inport;
+	u8  nports;
 	u8 *route_table;
 	u32 port_ok;
 	int (*add_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
@@ -251,6 +256,7 @@ struct rio_switch {
 			   u8 *sw_domain);
 	int (*em_init) (struct rio_dev *dev);
 	int (*em_handle) (struct rio_dev *dev, u8 swport);
+	struct rio_dev *nextdev[0];
 };
 
 /* Low-level architecture-dependent routines */
-- 
1.7.0.5

^ permalink raw reply related

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

The default error-stopped state handler provides recovery mechanism as defined
by RIO specification.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio.c             |  191 +++++++++++++++++++++++++++++++------
 drivers/rapidio/switches/idtcps.c |   10 ++
 drivers/rapidio/switches/tsi57x.c |    4 +
 include/linux/rio_regs.h          |    8 +-
 4 files changed, 179 insertions(+), 34 deletions(-)

diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 74e9d22..f58df11 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -495,6 +495,121 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
 }
 
 /**
+ * rio_clr_err_stopped - Clears port Error-stopped states.
+ * @rdev: Pointer to RIO device control structure
+ * @pnum: Switch port number to clear errors
+ * @err_status: port error status (if 0 reads register from device)
+ */
+static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status)
+{
+	struct rio_mport *mport = rdev->net->hport;
+	u16 destid = rdev->rswitch->destid;
+	u8 hopcount = rdev->rswitch->hopcount;
+	struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum];
+	u32 regval;
+	u32 far_ackid, far_linkstat, near_ackid;
+	int checkcount;
+
+	if (err_status == 0)
+		rio_mport_read_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+			&err_status);
+
+	if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) {
+		pr_debug("RIO_EM: servicing Output Error-Stopped state\n");
+		/*
+		 * Send a Link-Request/Input-Status control symbol
+		 */
+
+		/* Read from link maintenance response register
+		 * to clear valid bit */
+		rio_mport_read_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
+			&regval);
+		udelay(50);
+
+		/* Issue Input-status command */
+		rio_mport_write_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum),
+			RIO_MNT_REQ_CMD_IS);
+
+		checkcount = 3;
+		while (checkcount--) {
+			udelay(50);
+			rio_mport_read_config_32(mport, destid, hopcount,
+				rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
+				&regval);
+			if (regval & RIO_PORT_N_MNT_RSP_RVAL)
+				break;
+		}
+
+		pr_debug("RIO_EM: SP%d_MNT_RSP_CSR=0x%08x\n", pnum, regval);
+		if (regval & RIO_PORT_N_MNT_RSP_RVAL) {
+			far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5;
+			far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT;
+			rio_mport_read_config_32(mport, destid, hopcount,
+				rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum),
+				&regval);
+			pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n",
+				pnum, regval);
+			near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24;
+			pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x near_ackID=0x%02x\n",
+				pnum, far_ackid, far_linkstat, near_ackid);
+
+			/*
+			 * If required, synchronize ackIDs of near and
+			 * far sides.
+			 */
+			if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) ||
+			    (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) {
+				/* Align near outstanding/outbound ackIDs with
+				 * far inbound.
+				 */
+				rio_mport_write_config_32(mport, destid,
+					hopcount, rdev->phys_efptr +
+						RIO_PORT_N_ACK_STS_CSR(pnum),
+					(near_ackid << 24) |
+						(far_ackid << 8) | far_ackid);
+				/* Align far outstanding/outbound ackIDs with
+				 * near inbound.
+				 */
+				far_ackid++;
+				if (nextdev)
+					rio_write_config_32(nextdev,
+						nextdev->phys_efptr +
+						RIO_PORT_N_ACK_STS_CSR(nextdev->rswitch->inport),
+						(far_ackid << 24) |
+						(near_ackid << 8) | near_ackid);
+				else
+					pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n");
+			}
+		}
+
+		rio_mport_read_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+			&err_status);
+		pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
+	}
+
+	if ((err_status & RIO_PORT_N_ERR_STS_PW_INP_ES) && nextdev) {
+		pr_debug("RIO_EM: servicing Input Error-Stopped state\n");
+		rio_write_config_32(nextdev,
+			nextdev->phys_efptr +
+			RIO_PORT_N_MNT_REQ_CSR(nextdev->rswitch->inport),
+			RIO_MNT_REQ_CMD_IS);
+		udelay(50);
+
+		rio_mport_read_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+			&err_status);
+		pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
+	}
+
+	return (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
+			      RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 1 : 0;
+}
+
+/**
  * rio_inb_pwrite_handler - process inbound port-write message
  * @pw_msg: pointer to inbound port-write message
  *
@@ -507,7 +622,7 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 	struct rio_mport *mport;
 	u8 hopcount;
 	u16 destid;
-	u32 err_status;
+	u32 err_status, em_perrdet, em_ltlerrdet;
 	int rc, portnum;
 
 	rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
@@ -524,12 +639,11 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 	{
 	u32 i;
 	for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) {
-			pr_debug("0x%02x: %08x %08x %08x %08x",
+			pr_debug("0x%02x: %08x %08x %08x %08x\n",
 				 i*4, pw_msg->raw[i], pw_msg->raw[i + 1],
 				 pw_msg->raw[i + 2], pw_msg->raw[i + 3]);
 			i += 4;
 	}
-	pr_debug("\n");
 	}
 #endif
 
@@ -573,29 +687,28 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 			&err_status);
 	pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status);
 
-	if (pw_msg->em.errdetect) {
-		pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
-			 portnum, pw_msg->em.errdetect);
-		/* Clear EM Port N Error Detect CSR */
-		rio_mport_write_config_32(mport, destid, hopcount,
-			rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
-	}
+	if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) {
 
-	if (pw_msg->em.ltlerrdet) {
-		pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
-			 pw_msg->em.ltlerrdet);
-		/* Clear EM L/T Layer Error Detect CSR */
-		rio_mport_write_config_32(mport, destid, hopcount,
-			rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
-	}
+		if (!(rdev->rswitch->port_ok & (1 << portnum))) {
+			rdev->rswitch->port_ok |= (1 << portnum);
+			rio_set_port_lockout(rdev, portnum, 0);
+			/* Schedule Insertion Service */
+			pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n",
+			       rio_name(rdev), portnum);
+		}
 
-	/* Clear Port Errors */
-	rio_mport_write_config_32(mport, destid, hopcount,
-			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
-			err_status & RIO_PORT_N_ERR_STS_CLR_MASK);
+		/* Clear error-stopped states (if reported).
+		 * Depending on the link partner state, two attempts
+		 * may be needed for successful recovery.
+		 */
+		if (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
+				  RIO_PORT_N_ERR_STS_PW_INP_ES)) {
+			if (rio_clr_err_stopped(rdev, portnum, err_status))
+				rio_clr_err_stopped(rdev, portnum, 0);
+		}
+	}  else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */
 
-	if (rdev->rswitch->port_ok & (1 << portnum)) {
-		if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) {
+		if (rdev->rswitch->port_ok & (1 << portnum)) {
 			rdev->rswitch->port_ok &= ~(1 << portnum);
 			rio_set_port_lockout(rdev, portnum, 1);
 
@@ -608,17 +721,33 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 			pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n",
 			       rio_name(rdev), portnum);
 		}
-	} else {
-		if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) {
-			rdev->rswitch->port_ok |= (1 << portnum);
-			rio_set_port_lockout(rdev, portnum, 0);
+	}
 
-			/* Schedule Insertion Service */
-			pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n",
-			       rio_name(rdev), portnum);
-		}
+	rio_mport_read_config_32(mport, destid, hopcount,
+		rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet);
+	if (em_perrdet) {
+		pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
+			 portnum, em_perrdet);
+		/* Clear EM Port N Error Detect CSR */
+		rio_mport_write_config_32(mport, destid, hopcount,
+			rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
+	}
+
+	rio_mport_read_config_32(mport, destid, hopcount,
+		rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet);
+	if (em_ltlerrdet) {
+		pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
+			 em_ltlerrdet);
+		/* Clear EM L/T Layer Error Detect CSR */
+		rio_mport_write_config_32(mport, destid, hopcount,
+			rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
 	}
 
+	/* Clear remaining error bits */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
+			err_status & RIO_PORT_N_ERR_STS_CLR_MASK);
+
 	/* Clear Port-Write Pending bit */
 	rio_mport_write_config_32(mport, destid, hopcount,
 			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c
index 2c790c1..fc9f637 100644
--- a/drivers/rapidio/switches/idtcps.c
+++ b/drivers/rapidio/switches/idtcps.c
@@ -117,6 +117,10 @@ idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
 
 static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
 {
+	struct rio_mport *mport = rdev->net->hport;
+	u16 destid = rdev->rswitch->destid;
+	u8 hopcount = rdev->rswitch->hopcount;
+
 	pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
 	rdev->rswitch->add_entry = idtcps_route_add_entry;
 	rdev->rswitch->get_entry = idtcps_route_get_entry;
@@ -126,6 +130,12 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
 	rdev->rswitch->em_init = NULL;
 	rdev->rswitch->em_handle = NULL;
 
+	if (do_enum) {
+		/* set TVAL = ~50us */
+		rio_mport_write_config_32(mport, destid, hopcount,
+			rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c
index d34df72..d9e9492 100644
--- a/drivers/rapidio/switches/tsi57x.c
+++ b/drivers/rapidio/switches/tsi57x.c
@@ -205,6 +205,10 @@ tsi57x_em_init(struct rio_dev *rdev)
 			portnum++;
 	}
 
+	/* set TVAL = ~50us */
+	rio_mport_write_config_32(mport, destid, hopcount,
+		rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8);
+
 	return 0;
 }
 
diff --git a/include/linux/rio_regs.h b/include/linux/rio_regs.h
index aedee04..49a4dc7 100644
--- a/include/linux/rio_regs.h
+++ b/include/linux/rio_regs.h
@@ -222,15 +222,17 @@
 #define  RIO_PORT_GEN_MASTER		0x40000000
 #define  RIO_PORT_GEN_DISCOVERED	0x20000000
 #define RIO_PORT_N_MNT_REQ_CSR(x)	(0x0040 + x*0x20)	/* 0x0002 */
+#define  RIO_MNT_REQ_CMD_RD		0x03	/* Reset-device command */
+#define  RIO_MNT_REQ_CMD_IS		0x04	/* Input-status command */
 #define RIO_PORT_N_MNT_RSP_CSR(x)	(0x0044 + x*0x20)	/* 0x0002 */
 #define  RIO_PORT_N_MNT_RSP_RVAL	0x80000000 /* Response Valid */
 #define  RIO_PORT_N_MNT_RSP_ASTAT	0x000003e0 /* ackID Status */
 #define  RIO_PORT_N_MNT_RSP_LSTAT	0x0000001f /* Link Status */
 #define RIO_PORT_N_ACK_STS_CSR(x)	(0x0048 + x*0x20)	/* 0x0002 */
 #define  RIO_PORT_N_ACK_CLEAR		0x80000000
-#define  RIO_PORT_N_ACK_INBOUND		0x1f000000
-#define  RIO_PORT_N_ACK_OUTSTAND	0x00001f00
-#define  RIO_PORT_N_ACK_OUTBOUND	0x0000001f
+#define  RIO_PORT_N_ACK_INBOUND		0x3f000000
+#define  RIO_PORT_N_ACK_OUTSTAND	0x00003f00
+#define  RIO_PORT_N_ACK_OUTBOUND	0x0000003f
 #define RIO_PORT_N_ERR_STS_CSR(x)	(0x0058 + x*0x20)
 #define  RIO_PORT_N_ERR_STS_PW_OUT_ES	0x00010000 /* Output Error-stopped */
 #define  RIO_PORT_N_ERR_STS_PW_INP_ES	0x00000100 /* Input Error-stopped */
-- 
1.7.0.5

^ permalink raw reply related

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

Add callback that allows to create/remove switch-specific sysfs attributes.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio-sysfs.c |   26 +++++++++++++++++++-------
 include/linux/rio.h         |    2 ++
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index 00b4756..bfc483b 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -40,9 +40,6 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch
 	char *str = buf;
 	int i;
 
-	if (!rdev->rswitch)
-		goto out;
-
 	for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
 			i++) {
 		if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
@@ -52,7 +49,6 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch
 			    rdev->rswitch->route_table[i]);
 	}
 
-      out:
 	return (str - buf);
 }
 
@@ -63,10 +59,11 @@ struct device_attribute rio_dev_attrs[] = {
 	__ATTR_RO(asm_did),
 	__ATTR_RO(asm_vid),
 	__ATTR_RO(asm_rev),
-	__ATTR_RO(routes),
 	__ATTR_NULL,
 };
 
+static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
+
 static ssize_t
 rio_read_config(struct file *filp, struct kobject *kobj,
 		struct bin_attribute *bin_attr,
@@ -218,7 +215,17 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev)
 {
 	int err = 0;
 
-	err = sysfs_create_bin_file(&rdev->dev.kobj, &rio_config_attr);
+	err = device_create_bin_file(&rdev->dev, &rio_config_attr);
+
+	if (!err && rdev->rswitch) {
+		err = device_create_file(&rdev->dev, &dev_attr_routes);
+		if (!err && rdev->rswitch->sw_sysfs)
+			err = rdev->rswitch->sw_sysfs(rdev, 1);
+	}
+
+	if (err)
+		pr_warning("RIO: Failed to create attribute file(s) for %s\n",
+			   rio_name(rdev));
 
 	return err;
 }
@@ -231,5 +238,10 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev)
  */
 void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
 {
-	sysfs_remove_bin_file(&rdev->dev.kobj, &rio_config_attr);
+	device_remove_bin_file(&rdev->dev, &rio_config_attr);
+	if (rdev->rswitch) {
+		device_remove_file(&rdev->dev, &dev_attr_routes);
+		if (rdev->rswitch->sw_sysfs)
+			rdev->rswitch->sw_sysfs(rdev, 0);
+	}
 }
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 754895c..8f19fb2 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -233,6 +233,7 @@ struct rio_net {
  * @get_domain: Callback for switch-specific domain get function
  * @em_init: Callback for switch-specific error management initialization function
  * @em_handle: Callback for switch-specific error management handler function
+ * @sw_sysfs: Callback that initializes switch-specific sysfs attributes
  * @nextdev: Array of per-port pointers to the next attached device
  */
 struct rio_switch {
@@ -256,6 +257,7 @@ struct rio_switch {
 			   u8 *sw_domain);
 	int (*em_init) (struct rio_dev *dev);
 	int (*em_handle) (struct rio_dev *dev, u8 swport);
+	int (*sw_sysfs) (struct rio_dev *dev, int create);
 	struct rio_dev *nextdev[0];
 };
 
-- 
1.7.0.5

^ permalink raw reply related

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

Add check if PW message source device is accessible and change PW message
handler to recover if PW message source device is not available anymore (power
down or link disconnect).
To avoid possible loss of notification, the PW message handler scans the route
back from the source device to identify end of the broken link.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio.c |  111 ++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/rapidio/rio.h |    2 +
 2 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index f58df11..22f7847 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -495,6 +495,90 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
 }
 
 /**
+ * rio_chk_dev_route - Validate route to the specified device.
+ * @rdev: Pointer to RIO device control structure
+ * @nrdev: Pointer to last active device on the route to rdev
+ * @npnum: nrdev port number on the route to rdev
+ *
+ * Follows a route to the specified RIO device to determine the last available
+ * device (and corresponding RIO port) on the route.
+ */
+static int
+rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum)
+{
+	u32 result;
+	int p_port, rc = -EIO;
+	struct rio_dev *prev = NULL;
+
+	while (rdev->prev && (rdev->prev->pef & RIO_PEF_SWITCH)) {
+		if (rio_read_config_32(rdev->prev, RIO_DEV_ID_CAR, &result)) {
+			rdev = rdev->prev;
+			continue;
+		}
+
+		prev = rdev->prev;
+		for (p_port = 0; p_port < prev->rswitch->nports; p_port++)
+			if (prev->rswitch->nextdev[p_port] == rdev)
+				break;
+
+		if (p_port < prev->rswitch->nports) {
+			pr_debug("RIO: link failed on [%s]-P%d\n",
+				 rio_name(prev), p_port);
+			*nrdev = prev;
+			*npnum = p_port;
+			rc = 0;
+		} else {
+			pr_debug("RIO: failed to trace route to %s\n",
+				 rio_name(prev));
+		}
+
+		break;
+	}
+
+	return rc;
+}
+
+/**
+ * rio_mport_chk_dev_access - Validate access to the specified device.
+ * @mport: Master port to send transactions
+ * @destid: Device destination ID in network
+ * @hopcount: Number of hops into the network
+ */
+static int
+rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount)
+{
+	int i = 0;
+	u32 tmp;
+
+	while (rio_mport_read_config_32(mport, destid, hopcount,
+					RIO_DEV_ID_CAR, &tmp)) {
+		i++;
+		if (i == RIO_MAX_CHK_RETRY)
+			return -EIO;
+		mdelay(1);
+	}
+
+	return 0;
+}
+
+/**
+ * rio_chk_dev_access - Validate access to the specified device.
+ * @rdev: Pointer to RIO device control structure
+ */
+static int rio_chk_dev_access(struct rio_dev *rdev)
+{
+	u8 hopcount = 0xff;
+	u16 destid = rdev->destid;
+
+	if (rdev->rswitch) {
+		destid = rdev->rswitch->destid;
+		hopcount = rdev->rswitch->hopcount;
+	}
+
+	return rio_mport_chk_dev_access(rdev->net->hport, destid, hopcount);
+}
+
+/**
  * rio_clr_err_stopped - Clears port Error-stopped states.
  * @rdev: Pointer to RIO device control structure
  * @pnum: Switch port number to clear errors
@@ -627,8 +711,8 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 
 	rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
 	if (rdev == NULL) {
-		/* Someting bad here (probably enumeration error) */
-		pr_err("RIO: %s No matching device for CTag 0x%08x\n",
+		/* Device removed or enumeration error */
+		pr_debug("RIO: %s No matching device for CTag 0x%08x\n",
 			__func__, pw_msg->em.comptag);
 		return -EIO;
 	}
@@ -659,6 +743,26 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 			return 0;
 	}
 
+	portnum = pw_msg->em.is_port & 0xFF;
+
+	/* Check if device and route to it are functional:
+	 * Sometimes devices may send PW message(s) just before being
+	 * powered down (or link being lost).
+	 */
+	if (rio_chk_dev_access(rdev)) {
+		pr_debug("RIO: device access failed - get link partner\n");
+		/* Scan route to the device and identify failed link.
+		 * This will replace device and port reported in PW message.
+		 * PW message should not be used after this point.
+		 */
+		if (rio_chk_dev_route(rdev, &rdev, &portnum)) {
+			pr_err("RIO: Route trace for %s failed\n",
+				rio_name(rdev));
+			return -EIO;
+		}
+		pw_msg = NULL;
+	}
+
 	/* For End-point devices processing stops here */
 	if (!(rdev->pef & RIO_PEF_SWITCH))
 		return 0;
@@ -676,9 +780,6 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
 	/*
 	 * Process the port-write notification from switch
 	 */
-
-	portnum = pw_msg->em.is_port & 0xFF;
-
 	if (rdev->rswitch->em_handle)
 		rdev->rswitch->em_handle(rdev, portnum);
 
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
index f27b7a9..bc71ba1 100644
--- a/drivers/rapidio/rio.h
+++ b/drivers/rapidio/rio.h
@@ -14,6 +14,8 @@
 #include <linux/list.h>
 #include <linux/rio.h>
 
+#define RIO_MAX_CHK_RETRY	3
+
 /* Functions internal to the RIO core code */
 
 extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid,
-- 
1.7.0.5

^ permalink raw reply related

* [PATCH 8/9] RapidIO: Add device access check into the enumeration
From: Alexandre Bounine @ 2010-08-13 15:18 UTC (permalink / raw)
  To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1281712686-31308-1-git-send-email-alexandre.bounine@idt.com>

Add explicit device access check before performing device enumeration.
This gives a chance to clear possible link error conditions by issuing safe
maintenance read request(s).

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/rio-scan.c |    6 ++++++
 drivers/rapidio/rio.c      |    2 +-
 drivers/rapidio/rio.h      |    2 ++
 3 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 5dc33d1..fb5324b 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -761,6 +761,12 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 	u16 destid;
 	int tmp;
 
+	if (rio_mport_chk_dev_access(port,
+			RIO_ANY_DESTID(port->sys_size), hopcount)) {
+		pr_debug("RIO: device access check failed\n");
+		return -1;
+	}
+
 	if (rio_get_host_deviceid_lock(port, hopcount) == port->host_deviceid) {
 		pr_debug("RIO: PE already discovered by this host\n");
 		/*
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 22f7847..9621a7b 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -544,7 +544,7 @@ rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum)
  * @destid: Device destination ID in network
  * @hopcount: Number of hops into the network
  */
-static int
+int
 rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount)
 {
 	int i = 0;
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
index bc71ba1..d249a12 100644
--- a/drivers/rapidio/rio.h
+++ b/drivers/rapidio/rio.h
@@ -24,6 +24,8 @@ extern u32 rio_mport_get_physefb(struct rio_mport *port, int local,
 				 u16 destid, u8 hopcount);
 extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
 			     u8 hopcount, u32 from);
+extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid,
+				    u8 hopcount);
 extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
 extern int rio_enum_mport(struct rio_mport *mport);
 extern int rio_disc_mport(struct rio_mport *mport);
-- 
1.7.0.5

^ permalink raw reply related

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

Add the RIO switch driver and definitions for IDT CPS-1848 and CPS-1616 Gen2
devices.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Reviewed-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
---
 drivers/rapidio/switches/Kconfig    |    7 +
 drivers/rapidio/switches/Makefile   |    1 +
 drivers/rapidio/switches/idt_gen2.c |  439 +++++++++++++++++++++++++++++++++++
 include/linux/rio_ids.h             |    2 +
 include/linux/rio_regs.h            |    4 +
 5 files changed, 453 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rapidio/switches/idt_gen2.c

diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig
index 2b4e9b2..f47fee5 100644
--- a/drivers/rapidio/switches/Kconfig
+++ b/drivers/rapidio/switches/Kconfig
@@ -20,6 +20,13 @@ config RAPIDIO_TSI568
 	---help---
 	  Includes support for IDT Tsi568 serial RapidIO switch.
 
+config RAPIDIO_CPS_GEN2
+	bool "IDT CPS Gen.2 SRIO switch support"
+	depends on RAPIDIO
+	default n
+	---help---
+	  Includes support for ITD CPS Gen.2 serial RapidIO switches.
+
 config RAPIDIO_TSI500
 	bool "Tsi500 Parallel RapidIO switch support"
 	depends on RAPIDIO
diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile
index fe4adc3..48d67a6 100644
--- a/drivers/rapidio/switches/Makefile
+++ b/drivers/rapidio/switches/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_RAPIDIO_TSI57X)	+= tsi57x.o
 obj-$(CONFIG_RAPIDIO_CPS_XX)	+= idtcps.o
 obj-$(CONFIG_RAPIDIO_TSI568)	+= tsi568.o
 obj-$(CONFIG_RAPIDIO_TSI500)	+= tsi500.o
+obj-$(CONFIG_RAPIDIO_CPS_GEN2)	+= idt_gen2.o
 
 ifeq ($(CONFIG_RAPIDIO_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
new file mode 100644
index 0000000..0de4a9c
--- /dev/null
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -0,0 +1,439 @@
+/*
+ * IDT CPS Gen.2 Serial RapidIO switch family support
+ *
+ * Copyright 2010 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ *
+ * 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.
+ */
+
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/rio_ids.h>
+#include <linux/delay.h>
+#include "../rio.h"
+
+#define LOCAL_RTE_CONF_DESTID_SEL	0x010070
+#define LOCAL_RTE_CONF_DESTID_SEL_PSEL	0x0000001f
+
+#define IDT_LT_ERR_REPORT_EN	0x03100c
+
+#define IDT_PORT_ERR_REPORT_EN(n)	(0x031044 + (n)*0x40)
+#define IDT_PORT_ERR_REPORT_EN_BC	0x03ff04
+
+#define IDT_PORT_ISERR_REPORT_EN(n)	(0x03104C + (n)*0x40)
+#define IDT_PORT_ISERR_REPORT_EN_BC	0x03ff0c
+#define IDT_PORT_INIT_TX_ACQUIRED	0x00000020
+
+#define IDT_LANE_ERR_REPORT_EN(n)	(0x038010 + (n)*0x100)
+#define IDT_LANE_ERR_REPORT_EN_BC	0x03ff10
+
+#define IDT_DEV_CTRL_1		0xf2000c
+#define IDT_DEV_CTRL_1_GENPW		0x02000000
+#define IDT_DEV_CTRL_1_PRSTBEH		0x00000001
+
+#define IDT_CFGBLK_ERR_CAPTURE_EN	0x020008
+#define IDT_CFGBLK_ERR_REPORT		0xf20014
+#define IDT_CFGBLK_ERR_REPORT_GENPW		0x00000002
+
+#define IDT_AUX_PORT_ERR_CAP_EN	0x020000
+#define IDT_AUX_ERR_REPORT_EN	0xf20018
+#define IDT_AUX_PORT_ERR_LOG_I2C	0x00000002
+#define IDT_AUX_PORT_ERR_LOG_JTAG	0x00000001
+
+#define	IDT_ISLTL_ADDRESS_CAP	0x021014
+
+#define IDT_RIO_DOMAIN		0xf20020
+#define IDT_RIO_DOMAIN_MASK		0x000000ff
+
+#define IDT_PW_INFO_CSR		0xf20024
+
+#define IDT_SOFT_RESET		0xf20040
+#define IDT_SOFT_RESET_REQ		0x00030097
+
+#define IDT_I2C_MCTRL		0xf20050
+#define IDT_I2C_MCTRL_GENPW		0x04000000
+
+#define IDT_JTAG_CTRL		0xf2005c
+#define IDT_JTAG_CTRL_GENPW		0x00000002
+
+#define IDT_LANE_CTRL(n)	(0xff8000 + (n)*0x100)
+#define IDT_LANE_CTRL_BC	0xffff00
+#define IDT_LANE_CTRL_GENPW		0x00200000
+#define IDT_LANE_DFE_1_BC	0xffff18
+#define IDT_LANE_DFE_2_BC	0xffff1c
+
+#define IDT_PORT_OPS(n)		(0xf40004 + (n)*0x100)
+#define IDT_PORT_OPS_GENPW		0x08000000
+#define IDT_PORT_OPS_PL_ELOG		0x00000040
+#define IDT_PORT_OPS_LL_ELOG		0x00000020
+#define IDT_PORT_OPS_LT_ELOG		0x00000010
+#define IDT_PORT_OPS_BC		0xf4ff04
+
+#define IDT_PORT_ISERR_DET(n)	(0xf40008 + (n)*0x100)
+
+#define IDT_ERR_CAP		0xfd0000
+#define IDT_ERR_CAP_LOG_OVERWR		0x00000004
+
+#define IDT_ERR_RD		0xfd0004
+
+#define IDT_DEFAULT_ROUTE	0xde
+#define IDT_NO_ROUTE		0xdf
+
+static int
+idtg2_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+		       u16 table, u16 route_destid, u8 route_port)
+{
+	/*
+	 * Select routing table to update
+	 */
+	if (table == RIO_GLOBAL_TABLE)
+		table = 0;
+	else
+		table++;
+
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  LOCAL_RTE_CONF_DESTID_SEL, table);
+
+	/*
+	 * Program destination port for the specified destID
+	 */
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  RIO_STD_RTE_CONF_DESTID_SEL_CSR,
+				  (u32)route_destid);
+
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  RIO_STD_RTE_CONF_PORT_SEL_CSR,
+				  (u32)route_port);
+
+	return 0;
+}
+
+static int
+idtg2_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+		       u16 table, u16 route_destid, u8 *route_port)
+{
+	u32 result;
+
+	/*
+	 * Select routing table to read
+	 */
+	if (table == RIO_GLOBAL_TABLE)
+		table = 0;
+	else
+		table++;
+
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  LOCAL_RTE_CONF_DESTID_SEL, table);
+
+
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  RIO_STD_RTE_CONF_DESTID_SEL_CSR,
+				  route_destid);
+
+	rio_mport_read_config_32(mport, destid, hopcount,
+				 RIO_STD_RTE_CONF_PORT_SEL_CSR, &result);
+
+	if (IDT_DEFAULT_ROUTE == (u8)result || IDT_NO_ROUTE == (u8)result)
+		*route_port = RIO_INVALID_ROUTE;
+	else
+		*route_port = (u8)result;
+
+
+	return 0;
+}
+
+static int
+idtg2_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
+		       u16 table)
+{
+	u32 i;
+
+	/*
+	 * Select routing table to read
+	 */
+	if (table == RIO_GLOBAL_TABLE)
+		table = 0;
+	else
+		table++;
+
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  LOCAL_RTE_CONF_DESTID_SEL, table);
+
+	for (i = 0x80000000; i <= 0x800000ff;) {
+		rio_mport_write_config_32(mport, destid, hopcount,
+			RIO_STD_RTE_CONF_DESTID_SEL_CSR, i);
+		rio_mport_write_config_32(mport, destid, hopcount,
+			RIO_STD_RTE_CONF_PORT_SEL_CSR,
+			(IDT_DEFAULT_ROUTE << 24) | (IDT_DEFAULT_ROUTE << 16) |
+			(IDT_DEFAULT_ROUTE << 8) | IDT_DEFAULT_ROUTE);
+		i += 4;
+	}
+
+	return 0;
+}
+
+
+static int
+idtg2_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+		       u8 sw_domain)
+{
+	/*
+	 * Switch domain configuration operates only at global level
+	 */
+	rio_mport_write_config_32(mport, destid, hopcount,
+				  IDT_RIO_DOMAIN, (u32)sw_domain);
+	return 0;
+}
+
+static int
+idtg2_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+		       u8 *sw_domain)
+{
+	u32 regval;
+
+	/*
+	 * Switch domain configuration operates only at global level
+	 */
+	rio_mport_read_config_32(mport, destid, hopcount,
+				IDT_RIO_DOMAIN, &regval);
+
+	*sw_domain = (u8)(regval & 0xff);
+
+	return 0;
+}
+
+static int
+idtg2_em_init(struct rio_dev *rdev)
+{
+	struct rio_mport *mport = rdev->net->hport;
+	u16 destid = rdev->rswitch->destid;
+	u8 hopcount = rdev->rswitch->hopcount;
+	u32 regval;
+
+	/*
+	 * This routine performs device-specific initialization only.
+	 * All standard EM configuration should be performed at upper level.
+	 */
+
+	pr_debug("RIO: %s [%d:%d]\n", __func__, destid, hopcount);
+
+	/* Set Port-Write info CSR: PRIO=3 and CRF=1 */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_PW_INFO_CSR, 0x0000e000);
+
+	/*
+	 * Configure LT LAYER error reporting.
+	 */
+
+	/* Enable standard (RIO.p8) error reporting */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_LT_ERR_REPORT_EN,
+			REM_LTL_ERR_ILLTRAN | REM_LTL_ERR_UNSOLR |
+			REM_LTL_ERR_UNSUPTR);
+
+	/* Use Port-Writes for LT layer error reporting.
+	 * Enable per-port reset
+	 */
+	rio_mport_read_config_32(mport, destid, hopcount,
+			IDT_DEV_CTRL_1, &regval);
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_DEV_CTRL_1,
+			regval | IDT_DEV_CTRL_1_GENPW | IDT_DEV_CTRL_1_PRSTBEH);
+
+	/*
+	 * Configure PORT error reporting.
+	 */
+
+	/* Report all RIO.p8 errors supported by device */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_PORT_ERR_REPORT_EN_BC, 0x807e8037);
+
+	/* Configure repoting of implementation specific errors/events */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_PORT_ISERR_REPORT_EN_BC, IDT_PORT_INIT_TX_ACQUIRED);
+
+	/* Use Port-Writes for port error reporting and enable error logging */
+	rio_mport_read_config_32(mport, destid, hopcount,
+			IDT_PORT_OPS_BC, &regval);
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_PORT_OPS_BC, regval | IDT_PORT_OPS_GENPW |
+			IDT_PORT_OPS_PL_ELOG |
+			IDT_PORT_OPS_LL_ELOG |
+			IDT_PORT_OPS_LT_ELOG);
+	/* Overwrite error log if full */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_ERR_CAP, IDT_ERR_CAP_LOG_OVERWR);
+
+	/*
+	 * Configure LANE error reporting.
+	 */
+
+	/* Disable line error reporting */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_LANE_ERR_REPORT_EN_BC, 0);
+
+	/* Use Port-Writes for lane error reporting (when enabled) */
+	rio_mport_read_config_32(mport, destid, hopcount,
+			IDT_LANE_CTRL_BC, &regval);
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_LANE_CTRL_BC, regval | IDT_LANE_CTRL_GENPW);
+
+	/*
+	 * Configure AUX error reporting.
+	 */
+
+	/* Disable JTAG and I2C Error capture */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_AUX_PORT_ERR_CAP_EN, 0);
+
+	/* Disable JTAG and I2C Error reporting/logging */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_AUX_ERR_REPORT_EN, 0);
+
+	/* Disable Port-Write notification from JTAG */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_JTAG_CTRL, 0);
+
+	/* Disable Port-Write notification from I2C */
+	rio_mport_read_config_32(mport, destid, hopcount,
+			IDT_I2C_MCTRL, &regval);
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_I2C_MCTRL,
+			regval & ~IDT_I2C_MCTRL_GENPW);
+
+	/*
+	 * Configure CFG_BLK error reporting.
+	 */
+
+	/* Disable Configuration Block error capture */
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_CFGBLK_ERR_CAPTURE_EN, 0);
+
+	/* Disable Port-Writes for Configuration Block error reporting */
+	rio_mport_read_config_32(mport, destid, hopcount,
+			IDT_CFGBLK_ERR_REPORT, &regval);
+	rio_mport_write_config_32(mport, destid, hopcount,
+			IDT_CFGBLK_ERR_REPORT,
+			regval & ~IDT_CFGBLK_ERR_REPORT_GENPW);
+
+	/* set TVAL = ~50us */
+	rio_mport_write_config_32(mport, destid, hopcount,
+		rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
+
+	return 0;
+}
+
+static int
+idtg2_em_handler(struct rio_dev *rdev, u8 portnum)
+{
+	struct rio_mport *mport = rdev->net->hport;
+	u16 destid = rdev->rswitch->destid;
+	u8 hopcount = rdev->rswitch->hopcount;
+	u32 regval, em_perrdet, em_ltlerrdet;
+
+	rio_mport_read_config_32(mport, destid, hopcount,
+		rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet);
+	if (em_ltlerrdet) {
+		/* Service Logical/Transport Layer Error(s) */
+		if (em_ltlerrdet & REM_LTL_ERR_IMPSPEC) {
+			/* Implementation specific error reported */
+			rio_mport_read_config_32(mport, destid, hopcount,
+					IDT_ISLTL_ADDRESS_CAP, &regval);
+
+			pr_debug("RIO: %s Implementation Specific LTL errors" \
+				 " 0x%x @(0x%x)\n",
+				 rio_name(rdev), em_ltlerrdet, regval);
+
+			/* Clear implementation specific address capture CSR */
+			rio_mport_write_config_32(mport, destid, hopcount,
+					IDT_ISLTL_ADDRESS_CAP, 0);
+
+		}
+	}
+
+	rio_mport_read_config_32(mport, destid, hopcount,
+		rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet);
+	if (em_perrdet) {
+		/* Service Port-Level Error(s) */
+		if (em_perrdet & REM_PED_IMPL_SPEC) {
+			/* Implementation Specific port error reported */
+
+			/* Get IS errors reported */
+			rio_mport_read_config_32(mport, destid, hopcount,
+					IDT_PORT_ISERR_DET(portnum), &regval);
+
+			pr_debug("RIO: %s Implementation Specific Port" \
+				 " errors 0x%x\n", rio_name(rdev), regval);
+
+			/* Clear all implementation specific events */
+			rio_mport_write_config_32(mport, destid, hopcount,
+					IDT_PORT_ISERR_DET(portnum), 0);
+		}
+	}
+
+	return 0;
+}
+
+static ssize_t
+idtg2_show_errlog(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct rio_dev *rdev = to_rio_dev(dev);
+	struct rio_mport *mport = rdev->net->hport;
+	u16 destid = rdev->rswitch->destid;
+	u8 hopcount = rdev->rswitch->hopcount;
+	ssize_t len = 0;
+	u32 regval;
+
+	while (!rio_mport_read_config_32(mport, destid, hopcount,
+					 IDT_ERR_RD, &regval)) {
+		if (!regval)    /* 0 = end of log */
+			break;
+		len += snprintf(buf + len, PAGE_SIZE - len,
+					"%08x\n", regval);
+		if (len >= (PAGE_SIZE - 10))
+			break;
+	}
+
+	return len;
+}
+
+static DEVICE_ATTR(errlog, S_IRUGO, idtg2_show_errlog, NULL);
+
+static int idtg2_sysfs(struct rio_dev *rdev, int create)
+{
+	struct device *dev = &rdev->dev;
+	int err = 0;
+
+	if (create) {
+		/* Initialize sysfs entries */
+		err = device_create_file(dev, &dev_attr_errlog);
+		if (err)
+			dev_err(dev, "Unable create sysfs errlog file\n");
+	} else
+		device_remove_file(dev, &dev_attr_errlog);
+
+
+	return err;
+}
+
+static int idtg2_switch_init(struct rio_dev *rdev, int do_enum)
+{
+	pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+	rdev->rswitch->add_entry = idtg2_route_add_entry;
+	rdev->rswitch->get_entry = idtg2_route_get_entry;
+	rdev->rswitch->clr_table = idtg2_route_clr_table;
+	rdev->rswitch->set_domain = idtg2_set_domain;
+	rdev->rswitch->get_domain = idtg2_get_domain;
+	rdev->rswitch->em_init = idtg2_em_init;
+	rdev->rswitch->em_handle = idtg2_em_handler;
+	rdev->rswitch->sw_sysfs = idtg2_sysfs;
+
+	return 0;
+}
+
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h
index db50e1c..ee7b6ad 100644
--- a/include/linux/rio_ids.h
+++ b/include/linux/rio_ids.h
@@ -34,5 +34,7 @@
 #define RIO_DID_IDTCPS16		0x035b
 #define RIO_DID_IDTCPS6Q		0x035f
 #define RIO_DID_IDTCPS10Q		0x035e
+#define RIO_DID_IDTCPS1848		0x0374
+#define RIO_DID_IDTCPS1616		0x0379
 
 #endif				/* LINUX_RIO_IDS_H */
diff --git a/include/linux/rio_regs.h b/include/linux/rio_regs.h
index 49a4dc7..7f98fb6 100644
--- a/include/linux/rio_regs.h
+++ b/include/linux/rio_regs.h
@@ -263,6 +263,10 @@
 #define RIO_EM_EFB_HEADER	0x000	/* Error Management Extensions Block Header */
 #define RIO_EM_LTL_ERR_DETECT	0x008	/* Logical/Transport Layer Error Detect CSR */
 #define RIO_EM_LTL_ERR_EN	0x00c	/* Logical/Transport Layer Error Enable CSR */
+#define  REM_LTL_ERR_ILLTRAN		0x08000000 /* Illegal Transaction decode */
+#define  REM_LTL_ERR_UNSOLR		0x00800000 /* Unsolicited Response */
+#define  REM_LTL_ERR_UNSUPTR		0x00400000 /* Unsupported Transaction */
+#define  REM_LTL_ERR_IMPSPEC		0x000000ff /* Implementation Specific */
 #define RIO_EM_LTL_HIADDR_CAP	0x010	/* Logical/Transport Layer High Address Capture CSR */
 #define RIO_EM_LTL_ADDR_CAP	0x014	/* Logical/Transport Layer Address Capture CSR */
 #define RIO_EM_LTL_DEVID_CAP	0x018	/* Logical/Transport Layer Device ID Capture CSR */
-- 
1.7.0.5

^ permalink raw reply related

* help with kernel panics in task swapper on 460ex
From: Ayman El-Khashab @ 2010-08-13 20:36 UTC (permalink / raw)
  To: linuxppc-dev

I have an odd problem when using the 460ex rev b processors.  Previously,
I'd used the rev A without any issues on the same pcbs.  This happens on
multiple units now.  Basically, while running the system will just 
randomly kernel panic.  We have seen this probably 4 or 5 times on a 
over the course of several days per board.  On many occasions, the CPU 
will not kernel dump at all and will just be dead after having run for
some amount of time.  The kernel is 2.6.31.5

The exact details (though I don't think they are too useful are as follows.
The one interesting item if I read it correctly, is that the CPU was 
trying to get instructions from c002xxxx address.  However, this board only
has 512MB of memory so it seems that the address (if it is physical) isn't 
valid.  

Are there any tricks we can use to analyze this problem?  Or is there any
information we can collect to help pinpoint where this issue might lie?

Thanks 
ame

Unable to handle kernel paging request for data at address 0x8072f938
Faulting instruction address: 0xc002eac4
Oops: Kernel access of bad area, sig: 11 [#1]
PowerPC 44x Platform
Modules linked in: mapper
NIP: c002eac4 LR: c004811c CTR: c000add0
REGS: c038be60 TRAP: 0300   Not tainted  (2.6.31.5)
MSR: 00021000 <ME,CE>  CR: 24644324  XER: 00000000
DEAR: 8072f938, ESR: 00000000
TASK = c036a318[0] 'swapper' THREAD: c038a000
GPR00: 000000f6 c038bf10 c036a318 fffef700 fffef6f9 000000f9 000000f6 00061583
GPR08: c0398070 8072f930 c0398070 c03978c0 004ffffe 415ec006 1ffb3300 00000000
GPR16: 1ffa6e70 1ffad780 00000000 00000000 00000000 c036b1a0 ff1ad8aa c038bf48
GPR24: 00029000 0000001c 262a1965 c036de50 c039269c fffef700 fffef700 2625a000
NIP [c002eac4] get_next_timer_interrupt+0x30/0x260
LR [c004811c] tick_nohz_stop_sched_tick+0x110/0x404
Call Trace:
[c038bf10] [c003df74] ktime_get+0x1c/0x44 (unreliable)
[c038bf40] [c004811c] tick_nohz_stop_sched_tick+0x110/0x404
[c038bf90] [c0006f00] cpu_idle+0x50/0xd8
[c038bfb0] [c000197c] rest_init+0x5c/0x70
[c038bfc0] [c033f844] start_kernel+0x224/0x2a0
[c038bff0] [c0000200] skpinv+0x190/0x1cc
Instruction dump:
9421ffd0 7c0802a6 bfa10024 90010034 3d20c037 8169d1b8 7c7e1b78 808b0004
5485063e 7ca62b78 54c91838 7d295a14 <81090008> 39290008 48000014 80080014
---[ end trace 13e96cd635551e49 ]---
Kernel panic - not syncing: Attempted to kill the idle task!
Rebooting in 1 seconds..aymane@vbox:~$ 

^ permalink raw reply

* Re: help with kernel panics in task swapper on 460ex
From: Benjamin Herrenschmidt @ 2010-08-13 23:44 UTC (permalink / raw)
  To: Ayman El-Khashab; +Cc: linuxppc-dev
In-Reply-To: <20100813203636.GA11899@crust.elkhashab.com>

On Fri, 2010-08-13 at 15:36 -0500, Ayman El-Khashab wrote:
> 
> The exact details (though I don't think they are too useful are as follows.
> The one interesting item if I read it correctly, is that the CPU was 
> trying to get instructions from c002xxxx address.  However, this board only
> has 512MB of memory so it seems that the address (if it is physical) isn't 
> valid.  

Actually this is a perfectly valid virtual address. The kernel maps
memory at 0xc0000000 (at least the first 768M of it, the rest is
dynamically mapped in/out).

> Are there any tricks we can use to analyze this problem?  Or is there any
> information we can collect to help pinpoint where this issue might lie?

Well, xmon is your friend ...

Ben.

^ permalink raw reply

* Re: [PATCH 08/12] ptp: Added a brand new class driver for ptp clocks.
From: Grant Likely @ 2010-08-13 23:54 UTC (permalink / raw)
  To: Richard Cochran
  Cc: netdev, devicetree-discuss, Thomas Gleixner, linuxppc-dev,
	linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <20100813093438.GA18112@riccoc20.at.omicron.at>

On Fri, Aug 13, 2010 at 3:34 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> On Tue, Jun 15, 2010 at 01:11:30PM -0600, Grant Likely wrote:
>> On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
>> > +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
>>
>> Doesn't appear that clocks is manipulated at atomic context. =A0Mutex in=
stead?
> ...
>> If the spinlock is changed to a mutex that is held for the entire
>> function call, then the logic here can be simpler.
>
> Grant,
>
> I am working on another go at this patch series. Stupid question:
>
> The caller of ptp_clock_register(), which takes the clocks_lock, is
> always a module_init() function. Is this always a safe context in
> which to call mutex_lock?

Yes, you can take mutexes in the module_init context.

g.

--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [Resend][PATCHv3] Xilinx Virtex 4 FX Soft FPU support
From: Sergey Temerkhanov @ 2010-08-14  4:09 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: John Linn

This patch enables support for Xilinx Virtex 4 FX singe-float FPU.

Changelog v2-v3:
	-Fixed whitespaces for SAVE_FPR/REST_FPR.
	-Changed description of MSR_AP bit.
	-Removed the stub for APU unavailable exception.

Changelog v1->v2:
	-Added MSR_AP bit definition
	-Renamed CONFIG_XILINX_FPU to CONFIG_XILINX_SOFTFPU, moved it to
	 'Platform support' and made it Virtex4-FX-only.
	-Changed SAVE_FPR/REST_FPR definition style.

Caveats:
	- Hard-float binaries which rely on in-kernel math emulation will
	give wrong results since they expect 64-bit double-precision instead of
	32-bit single-precision numbers which Xilinx V4-FX Soft FPU produces.


Signed-off-by: Sergey Temerkhanov<temerkhanov@cifronik.ru>

diff -r 626de0d94469 arch/powerpc/include/asm/ppc_asm.h
--- a/arch/powerpc/include/asm/ppc_asm.h	Wed May 26 15:33:32 2010 +0400
+++ b/arch/powerpc/include/asm/ppc_asm.h	Wed May 26 20:30:43 2010 +0400
@@ -85,13 +85,21 @@
 #define REST_8GPRS(n, base)	REST_4GPRS(n, base); REST_4GPRS(n+4, base)
 #define REST_10GPRS(n, base)	REST_8GPRS(n, base); REST_2GPRS(n+8, base)
 
+
+#ifdef CONFIG_XILINX_SOFTFPU
+#define SAVE_FPR(n, base)	stfs	n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base)
+#define REST_FPR(n, base)	lfs	n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base)
+#else
 #define SAVE_FPR(n, base)	stfd	n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base)
+#define REST_FPR(n, base)	lfd	n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base)
+#endif
+
 #define SAVE_2FPRS(n, base)	SAVE_FPR(n, base); SAVE_FPR(n+1, base)
 #define SAVE_4FPRS(n, base)	SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
 #define SAVE_8FPRS(n, base)	SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
 #define SAVE_16FPRS(n, base)	SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
 #define SAVE_32FPRS(n, base)	SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
-#define REST_FPR(n, base)	lfd	n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base)
+
 #define REST_2FPRS(n, base)	REST_FPR(n, base); REST_FPR(n+1, base)
 #define REST_4FPRS(n, base)	REST_2FPRS(n, base); REST_2FPRS(n+2, base)
 #define REST_8FPRS(n, base)	REST_4FPRS(n, base); REST_4FPRS(n+4, base)
diff -r 626de0d94469 arch/powerpc/include/asm/reg.h
--- a/arch/powerpc/include/asm/reg.h	Wed May 26 15:33:32 2010 +0400
+++ b/arch/powerpc/include/asm/reg.h	Wed May 26 20:30:43 2010 +0400
@@ -30,6 +30,7 @@
 #define MSR_ISF_LG	61              /* Interrupt 64b mode valid on 630 */
 #define MSR_HV_LG 	60              /* Hypervisor state */
 #define MSR_VEC_LG	25	        /* Enable AltiVec */
+#define MSR_AP_LG	25	        /* Enable PPC405 APU */
 #define MSR_VSX_LG	23		/* Enable VSX */
 #define MSR_POW_LG	18		/* Enable Power Management */
 #define MSR_WE_LG	18		/* Wait State Enable */
@@ -71,6 +72,7 @@
 #define MSR_HV		0
 #endif
 
+#define MSR_AP		__MASK(MSR_AP_LG)	/* Enable PPC405 APU */
 #define MSR_VEC		__MASK(MSR_VEC_LG)	/* Enable AltiVec */
 #define MSR_VSX		__MASK(MSR_VSX_LG)	/* Enable VSX */
 #define MSR_POW		__MASK(MSR_POW_LG)	/* Enable Power Management */
diff -r 626de0d94469 arch/powerpc/kernel/fpu.S
--- a/arch/powerpc/kernel/fpu.S	Wed May 26 15:33:32 2010 +0400
+++ b/arch/powerpc/kernel/fpu.S	Wed May 26 20:30:43 2010 +0400
@@ -57,6 +57,9 @@
 _GLOBAL(load_up_fpu)
 	mfmsr	r5
 	ori	r5,r5,MSR_FP
+#ifdef CONFIG_XILINX_SOFTFPU
+	oris	r5,r5,MSR_AP@h
+#endif
 #ifdef CONFIG_VSX
 BEGIN_FTR_SECTION
 	oris	r5,r5,MSR_VSX@h
@@ -85,6 +88,9 @@
 	toreal(r5)
 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 	li	r10,MSR_FP|MSR_FE0|MSR_FE1
+#ifdef CONFIG_XILINX_SOFTFPU
+	oris	r10,r10,MSR_AP@h
+#endif
 	andc	r4,r4,r10		/* disable FP for previous task */
 	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 1:
@@ -94,6 +100,9 @@
 	mfspr	r5,SPRN_SPRG3		/* current task's THREAD (phys) */
 	lwz	r4,THREAD_FPEXC_MODE(r5)
 	ori	r9,r9,MSR_FP		/* enable FP for current */
+#ifdef CONFIG_XILINX_SOFTFPU
+	oris	r9,r9,MSR_AP@h
+#endif
 	or	r9,r9,r4
 #else
 	ld	r4,PACACURRENT(r13)
@@ -124,6 +133,9 @@
 _GLOBAL(giveup_fpu)
 	mfmsr	r5
 	ori	r5,r5,MSR_FP
+#ifdef CONFIG_XILINX_SOFTFPU
+	oris	r5,r5,MSR_AP@h
+#endif
 #ifdef CONFIG_VSX
 BEGIN_FTR_SECTION
 	oris	r5,r5,MSR_VSX@h
@@ -145,6 +157,9 @@
 	beq	1f
 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 	li	r3,MSR_FP|MSR_FE0|MSR_FE1
+#ifdef CONFIG_XILINX_SOFTFPU
+	oris	r3,r3,MSR_AP@h
+#endif
 #ifdef CONFIG_VSX
 BEGIN_FTR_SECTION
 	oris	r3,r3,MSR_VSX@h
diff -r 626de0d94469 arch/powerpc/kernel/head_40x.S
--- a/arch/powerpc/kernel/head_40x.S	Wed May 26 15:33:32 2010 +0400
+++ b/arch/powerpc/kernel/head_40x.S	Wed May 26 20:30:43 2010 +0400
@@ -420,7 +420,19 @@
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	EXC_XFER_STD(0x700, program_check_exception)
 
+/* 0x0800 - FPU unavailable Exception */
+#ifdef CONFIG_PPC_FPU
+	START_EXCEPTION(0x0800, FloatingPointUnavailable)
+	NORMAL_EXCEPTION_PROLOG
+	beq	1f;							      \
+	bl	load_up_fpu;		/* if from user, just load it up */   \
+	b	fast_exception_return;					      \
+1:	addi	r3,r1,STACK_FRAME_OVERHEAD;				      \
+	EXC_XFER_EE_LITE(0x800, kernel_fp_unavailable_exception)
+#else
 	EXCEPTION(0x0800, Trap_08, unknown_exception, EXC_XFER_EE)
+#endif
+
 	EXCEPTION(0x0900, Trap_09, unknown_exception, EXC_XFER_EE)
 	EXCEPTION(0x0A00, Trap_0A, unknown_exception, EXC_XFER_EE)
 	EXCEPTION(0x0B00, Trap_0B, unknown_exception, EXC_XFER_EE)
@@ -821,8 +833,10 @@
  * The PowerPC 4xx family of processors do not have an FPU, so this just
  * returns.
  */
+#ifndef CONFIG_PPC_FPU
 _ENTRY(giveup_fpu)
 	blr
+#endif
 
 /* This is where the main kernel code starts.
  */
diff -r 626de0d94469 arch/powerpc/platforms/Kconfig
--- a/arch/powerpc/platforms/Kconfig	Wed May 26 15:33:32 2010 +0400
+++ b/arch/powerpc/platforms/Kconfig	Wed May 26 20:30:43 2010 +0400
@@ -333,4 +333,9 @@
 	bool "Xilinx PCI host bridge support"
 	depends on PCI && XILINX_VIRTEX
 
+config XILINX_SOFTFPU
+	bool "Xilinx Soft FPU"
+	select PPC_FPU
+	depends on XILINX_VIRTEX_4_FX && !PPC40x_SIMPLE && !405GP && !405GPR
+
 endmenu

-- 
Regards, Sergey Temerkhanov

^ permalink raw reply

* Re: Questions about powerpc kernel tree
From: Terren Chow @ 2010-08-14  4:21 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1281413958.28245.17.camel@pasglop>

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

Do you mean the "powerpc kernel tree" function as the "next" git branch for
PPC architecture?

2010/8/10 Benjamin Herrenschmidt <benh@kernel.crashing.org>

> On Tue, 2010-08-10 at 10:39 +0800, Terren Chow wrote:
> > Hi Ben:
> >     I'm not quite clearly know about the powerpc kernel tree histroy.
> > why the tags in the powerpc kernel tree are only update to 2.6.26-rc9
> > at about 2 years ago?
>
> Not sure what you're looking at but your shouldn't need to use the
> "powerpc kernel tree" whatever tree this is :-) All our work goes into
> Linus tree on a regular basis, so unless you are specifically looking at
> the powerpc "next" git branch aimed at the next merge window, there
> should be nothing of interest to you there.
>
> Ben.
>
>
>


-- 
Terren Chow
College of informatics, SCAU
Graduate student
Lab of Embedded System and Wireless Sensor Network
MSN: terren.chow@hotmail.com

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

^ permalink raw reply

* Re: [lm-sensors] [PATCH] hwmon: (tmp421) Add nfactor support (2nd attempt)
From: Jean Delvare @ 2010-08-14 19:15 UTC (permalink / raw)
  To: Andre Prendel
  Cc: lm-sensors@lm-sensors.org, linuxppc-dev@lists.ozlabs.org,
	Guenter Roeck
In-Reply-To: <20100721194650.GA2271@andre-laptop>

Hi Andre, Guenter, Jeff,

On Wed, 21 Jul 2010 21:46:50 +0200, Andre Prendel wrote:
> On Tue, Jul 20, 2010 at 08:59:52AM -0700, Guenter Roeck wrote:
> > On Tue, Jul 20, 2010 at 11:09:53AM -0400, Andre Prendel wrote:
> > > On Thu, May 20, 2010 at 09:35:56PM +0200, Andre Prendel wrote:
> > > > On Thu, May 20, 2010 at 03:07:05PM -0400, Jeff Angielski wrote:
> > > > > In any event, here it is again:
> > > > 
> > > > Acked-by: Andre Prendel <andre.prendel@gmx.de>
> > > 
> > > Hi Jeff,
> > > 
> > > I'de suggest to resend the patch with my acked-by to the lm-sensors list and
> > > Andrew Morton. It looks like Jean is too busy at the moment.
> > > 
> > > Regards,
> > > Andre
> > >  
> > > > > >From 9acd29ff48c64e58a7f5cdb888c86e737c56281c Mon Sep 17 00:00:00 2001
> > > > > From: Jeff Angielski <jeff@theptrgroup.com>
> > > > > Date: Mon, 10 May 2010 10:26:34 -0400
> > > > > Subject: [PATCH] hwmon: (tmp421) Add nfactor support
> > > > > 
> > > > > Add support for reading and writing the n-factor correction
> > > > > registers.  This is needed to compensate for the characteristics
> > > > > of a particular sensor hanging off of the remote channels.
> > > > > 
> > 
> > My concerns with this approach are 
> > 
> > 1) It changes the sysfs abi. libsensors won't support it. It opens up
> >    a can of worms with everyone starting to put chip-specific extensions
> >    into the ABI. If such an extension has to be made, it should be a) really necessary
> >    and b) a generic extension which applies to all chips.
> 
> A chip-specific extension can't be also generic. So we have to decide whether weaccept chip-specific extensions or not.

libsensors 3 will never support chip-specific extensions. We've been
there with libsensors 2, it was a nightmare, never again please.

This doesn't mean we can't have chip-specific extensions, and actually
some drivers have such extensions already. But as they are often
undocumented and bound to be replaced by standard implementations in
the future, nobody should rely on them. Which makes their usefulness
dubious.

This is the reason why I always insist on trying to define a standard
suitable for all chips before you think of adding a not-yet-supported
feature to a given driver.

> > 2) My understanding is that value adjustments are supposed to be made via sensors.conf,
> >    and that values reported by the chip should always be 'raw', ie unadjusted
> >    values.
> > 
> > Instead of exporting n_adjust to the user via sysfs, it might make more sense 
> > to reset the correction factor to its default (if it was changed), and handle
> > required adjustments in sensors.conf.
> 
> Jeff, what do you think?

I'm not Jeff, but resetting the factors is a bad idea. The
BIOS/firmware might have set them up properly, so the default should be
to leave them untouched (as we do with every other setting.)

> > Even if Jean doesn't have time to handle the patch, you should at least get his Ack
> > for the ABI changes.
> 
> That was the intention of resending the patch to the lm-sensors list. It would
> be a pity to lose Jeff's effort.

I'm not giving my ack for any non-standard feature, sorry.

-- 
Jean Delvare

^ permalink raw reply

* [PATCH] powerpc: fix typo in uImage target
From: Anatolij Gustschin @ 2010-08-16  8:26 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Anatolij Gustschin, Sam Ravnborg, stable

Commit e32e78c5ee8aadef020fbaecbe6fb741ed9029fd
(powerpc: fix build with make 3.82) introduced a
typo in uImage target and broke building uImage:

make: *** No rule to make target `uImage'.  Stop.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: stable <stable@kernel.org>
---
 arch/powerpc/Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index e3ea151..b7212b6 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -164,7 +164,7 @@ drivers-$(CONFIG_OPROFILE)	+= arch/powerpc/oprofile/
 all: zImage
 
 # With make 3.82 we cannot mix normal and wildcard targets
-BOOT_TARGETS1 := zImage zImage.initrd uImaged
+BOOT_TARGETS1 := zImage zImage.initrd uImage
 BOOT_TARGETS2 := zImage% dtbImage% treeImage.% cuImage.% simpleImage.%
 
 PHONY += $(BOOT_TARGETS1) $(BOOT_TARGETS2)
-- 
1.7.0.4

^ permalink raw reply related

* Re: [PATCH] powerpc: fix typo in uImage target
From: Sam Ravnborg @ 2010-08-16  8:38 UTC (permalink / raw)
  To: Anatolij Gustschin; +Cc: linuxppc-dev, stable
In-Reply-To: <1281947216-15342-1-git-send-email-agust@denx.de>

On Mon, Aug 16, 2010 at 10:26:56AM +0200, Anatolij Gustschin wrote:
> Commit e32e78c5ee8aadef020fbaecbe6fb741ed9029fd
> (powerpc: fix build with make 3.82) introduced a
> typo in uImage target and broke building uImage:
> 
> make: *** No rule to make target `uImage'.  Stop.
> 
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> Cc: stable <stable@kernel.org>

blush...

	Sam

^ permalink raw reply

* JFFS2 question : Mounting /dev/mtdblock1 failed , NOR flash
From: hellohello @ 2010-08-16  9:44 UTC (permalink / raw)
  To: linuxppc-dev list

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




I have one NOR flash, sector size is 128k. 
I want to make jffs2 filesystem on it to store data.
I have used flash_eraseall -j to format it as jffs2.
But I meet the problem when mount the /dev/mtdblock1.
Thanks for any pointers or ideas.

-sh-2.05b# mount -t jffs2 /dev/mtdblock1 /mnt/jffs 
Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes
empty_blocks 0, bad_blocks 0, c->nr_blocks 42
mount: Mounting /dev/mtdblock1 on /mnt/jffs failed: Input/output error



I have used mtd_debug cmd to test, and result is right:

# cat /proc/mtd
dev:    size   erasesize  name
mtd1: 00540000 00020000 "jffs"
mtd2: 00180000 00020000 "others"

# mtd_debug erase /dev/mtd1 0x0 0x20000 
MTD_open
MTD_ioctl
Erased 131072 bytes from address 0x00000000 in flash
MTD_close
# dd if=/dev/zero of=file.img bs=1k count=128
128+0 records in
128+0 records out
# mtd_debug write /dev/mtdblock1 0x0 0x20000 file.img
Copied 131072 bytes from file.img to address 0x00000000 in flash
# mtd_debug read /dev/mtdblock1 0x0 0x20000 file_rd.img
Copied 131072 bytes from address 0x00000000 in flash to file_rd.img
# cmp -l file.img file_rd.img
# 



Here is the detail error info:



-sh-2.05b# flash_eraseall -j /dev/mtd1
MTD_open
MTD_ioctl
MTD_ioctl
Erasing 128 Kibyte @ 0 --  0 % MTD_ioctl
complete.MTD_write
ErasiMTD_ioctlen at 0.
ng 128 Kibyte @ 20000 --  2 % complete.MTD_write
EMTD_ioctlritten at 20000.
rasing 128 Kibyte @ 40000 --  4 % complete.MTD_write
EMTD_ioctlritten at 40000.
... ...
... ...
MTD_ioctlwritten at 500000.
Erasing 128 Kibyte @ 520000 -- 97 % complete.MTD_write
MTD_closewritten at 520000.
Erasing 128 Kibyte @ 540000 -- 100% complete.



-sh-2.05b# mount -t jffs2 /dev/mtdblock1 /mnt/jffs 
Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes
empty_blocks 0, bad_blocks 0, c->nr_blocks 42
mount: Mounting /dev/mtdblock1 on /mnt/jffs failed: Input/output error

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

^ permalink raw reply

* [PATCH v5 0/5] ptp: IEEE 1588 clock support
From: Richard Cochran @ 2010-08-16 11:17 UTC (permalink / raw)
  To: netdev
  Cc: linuxppc-dev, devicetree-discuss, linux-kernel, Rodolfo Giometti,
	linux-arm-kernel, Krzysztof Halasa

Now and again there has been talk on the netdev 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.

* Why all the CCs?

  1. IMHO, the patches should go through netdev.
  2. A reviewer on netdev said, this should appear on lkml.
  3. One driver is for PowerPC, and adds device tree stuff.
  4. One driver is for the ARM Xscale IXP465.

* Open Issues:

** DP83640
   In order to make this work, one line must be added into the MAC
   driver. If you have the DP83640 and want to try the driver, you
   need to add this one line to your MAC driver: In the
   .ndo_start_xmit function, add skb_tx_timestamp(skb).

** IXP465
   I do not know how to correctly choose the timestamp "channel" based
   on the port identifier:

+#define PORT2CHANNEL(p)		1
+/*
+ * PHYSICAL_ID(p->id) ?
+ * TODO - Figure out correct mapping.
+ */

   Krzysztof, can you help?

* PTP Patch ChangeLog
** v5
*** general
   - Added a hook into the PPS subsystem
   - Corrected max_adj in all drivers
   - Removed unnecessary sysfs stuff
   - Replaced spinlock with mutex in class driver
*** gianfar
   - Added PPS support
   - Changed underscore to minus in device tree bindings
   - Use of_iomap instead of ioremap
*** ixp465
   - Added an external trigger event
   - Corrected in_progress logic
*** phyter
   - Added an external trigger event
   - Added support for phy status frames

** v4
*** general
   - Added a clock driver for the National Semiconductor PHYTER.
   - Added a clock driver for the Intel IXP465.
   - Made more stylish according to checkstyle.pl.
*** gianfar
   - Replace device_type and model with compatible string ("fsl,etsec-ptp")
   - Register only one interrupt, since others are superfluous.
   - Combine ptp clock instance with private variable structure.
   - ISR now returns NONE or HANDLED properly.
   - Print error message if something is missing from the device nodes.

** 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 (5):
  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.
  ptp: Added a clock driver for the IXP46x.
  ptp: Added a clock driver for the National Semiconductor PHYTER.

 Documentation/powerpc/dts-bindings/fsl/tsec.txt |   57 ++
 Documentation/ptp/ptp.txt                       |   95 +++
 Documentation/ptp/testptp.c                     |  306 ++++++++
 Documentation/ptp/testptp.mk                    |   33 +
 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h   |   78 ++
 arch/powerpc/boot/dts/mpc8313erdb.dts           |   14 +
 arch/powerpc/boot/dts/mpc8572ds.dts             |   14 +
 arch/powerpc/boot/dts/p2020ds.dts               |   14 +
 arch/powerpc/boot/dts/p2020rdb.dts              |   14 +
 drivers/Kconfig                                 |    2 +
 drivers/Makefile                                |    1 +
 drivers/net/Makefile                            |    1 +
 drivers/net/arm/ixp4xx_eth.c                    |  191 +++++
 drivers/net/gianfar_ptp.c                       |  527 +++++++++++++
 drivers/net/gianfar_ptp_reg.h                   |  113 +++
 drivers/net/phy/Kconfig                         |   29 +
 drivers/net/phy/Makefile                        |    1 +
 drivers/net/phy/dp83640.c                       |  904 +++++++++++++++++++++++
 drivers/net/phy/dp83640_reg.h                   |  261 +++++++
 drivers/ptp/Kconfig                             |   65 ++
 drivers/ptp/Makefile                            |    7 +
 drivers/ptp/ptp_clock.c                         |  514 +++++++++++++
 drivers/ptp/ptp_ixp46x.c                        |  359 +++++++++
 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 +
 28 files changed, 3955 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 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
 create mode 100644 drivers/net/gianfar_ptp.c
 create mode 100644 drivers/net/gianfar_ptp_reg.h
 create mode 100644 drivers/net/phy/dp83640.c
 create mode 100644 drivers/net/phy/dp83640_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_ixp46x.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

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

This patch adds 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      |  306 ++++++++++++++++++++++
 Documentation/ptp/testptp.mk     |   33 +++
 drivers/Kconfig                  |    2 +
 drivers/Makefile                 |    1 +
 drivers/ptp/Kconfig              |   27 ++
 drivers/ptp/Makefile             |    5 +
 drivers/ptp/ptp_clock.c          |  514 ++++++++++++++++++++++++++++++++++++++
 include/linux/Kbuild             |    1 +
 include/linux/ptp_clock.h        |   79 ++++++
 include/linux/ptp_clock_kernel.h |  137 ++++++++++
 11 files changed, 1200 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..41a9839
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,306 @@
+/*
+ * 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"
+		" -p val     enable output with a period of 'val' nanoseconds\n"
+		" -P val     enable or disable (val=1|0) the system clock PPS\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 perout = -1;
+	int pps = -1;
+	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:ghp:P:st: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 'p':
+			perout = atoi(optarg);
+			break;
+		case 'P':
+			pps = atoi(optarg);
+			break;
+		case 's':
+			settime = 1;
+			break;
+		case 't':
+			adjtime = atoi(optarg);
+			break;
+		case 'v':
+			version = 1;
+			break;
+		case 'h':
+			usage(progname);
+			return 0;
+		case '?':
+		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("external time stamp request 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);
+			fflush(stdout);
+		}
+		/* 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();
+		}
+	}
+
+	if (perout >= 0) {
+		memset(&request, 0, sizeof(request));
+		request.type = PTP_REQUEST_PEROUT;
+		request.index = 0;
+		request.ts.tv_sec = 0;
+		request.ts.tv_nsec = perout;
+		request.flags = perout ? PTP_ENABLE_FEATURE : 0;
+		if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+			perror("PTP_FEATURE_REQUEST");
+			extts = 0;
+		} else {
+			puts("periodic output request okay");
+		}
+	}
+
+	if (pps != -1) {
+		memset(&request, 0, sizeof(request));
+		request.type = PTP_REQUEST_PPS;
+		request.flags = perout ? PTP_ENABLE_FEATURE : 0;
+		if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+			perror("PTP_FEATURE_REQUEST");
+		} else {
+			puts("pps for system time request okay");
+		}
+	}
+
+	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 91874e0..6d12b48 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,6 +76,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..cd7becb
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,27 @@
+#
+# PTP clock support configuration
+#
+
+menu "PTP clock support"
+
+config PTP_1588_CLOCK
+	tristate "PTP clock support"
+	depends on EXPERIMENTAL
+	depends on PPS
+	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..1ebb4b0
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,514 @@
+/*
+ * PTP 1588 clock support
+ *
+ * Partially adapted from the Linux PPS driver.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  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/pps_kernel.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
+#define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
+#define PTP_PPS_EVENT PPS_CAPTUREASSERT
+#define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
+
+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 */
+	int pps_source;
+
+	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_MUTEX(clocks_mux); /* 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);
+
+	if (info->n_alarm > PTP_MAX_ALARMS)
+		return ERR_PTR(-EINVAL);
+
+	/* Find a free clock slot and reserve it. */
+	err = -EBUSY;
+	mutex_lock(&clocks_mux);
+	index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
+	if (index < PTP_MAX_CLOCKS)
+		set_bit(index, clocks.map);
+	else
+		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;
+
+	/* Register a new PPS source. */
+	if (info->pps) {
+		struct pps_source_info pps;
+		memset(&pps, 0, sizeof(pps));
+		snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
+		pps.mode = PTP_PPS_MODE;
+		pps.owner = info->owner;
+		pps.dev = ptp->dev;
+		err = pps_register_source(&pps, PTP_PPS_DEFAULTS);
+		if (err < 0) {
+			pr_err("failed to register pps source\n");
+			goto no_pps;
+		} else
+			ptp->pps_source = err;
+	}
+
+	/* Clock is ready, add it into the list. */
+	list_add(&ptp->list, &clocks.list);
+
+	mutex_unlock(&clocks_mux);
+	return ptp;
+
+no_pps:
+no_cdev:
+	device_destroy(ptp_class, ptp->devid);
+no_device:
+	mutex_destroy(&ptp->alarm_mux);
+	mutex_destroy(&ptp->tsevq_mux);
+	kfree(ptp);
+no_memory:
+	clear_bit(index, clocks.map);
+no_clock:
+	mutex_unlock(&clocks_mux);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+	/* Release the clock's resources. */
+	if (ptp->info->pps)
+		pps_unregister_source(ptp->pps_source);
+	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. */
+	mutex_lock(&clocks_mux);
+	list_del(&ptp->list);
+	clear_bit(ptp->index, clocks.map);
+	mutex_unlock(&clocks_mux);
+
+	kfree(ptp);
+
+	return 0;
+}
+EXPORT_SYMBOL(ptp_clock_unregister);
+
+void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+	struct timespec ts;
+	struct pps_ktime pps_ts;
+
+	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:
+		getnstimeofday(&ts);
+		pps_ts.sec = ts.tv_sec;
+		pps_ts.nsec = ts.tv_nsec;
+		pps_event(ptp->pps_source, &pps_ts, PTP_PPS_EVENT, NULL);
+		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;
+	s32 ppb;
+
+	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;
+		ppb = arg;
+		if (ppb > ops->max_adj || ppb < -ops->max_adj)
+			return -EINVAL;
+		err = ops->adjfreq(priv, ppb);
+		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,
+};
+
+/* 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;
+	}
+
+	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 9aa9bca..471205a 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..d6cc158
--- /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.7.0.4

^ permalink raw reply related

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

This 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 cd7becb..3aa517a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -24,4 +24,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..f93ae0c
--- /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 c631168..eba3bcf 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.7.0.4

^ permalink raw reply related

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

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

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

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

^ permalink raw reply related


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