LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] ehea: fix sysfs link compile problem
From: Jeff Garzik @ 2008-02-01 18:40 UTC (permalink / raw)
  To: Jan-Bernd Themann
  Cc: Thomas Klein, Jan-Bernd Themann, netdev, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder, Stefan Roscher
In-Reply-To: <200802011537.42578.ossthema@de.ibm.com>

Jan-Bernd Themann wrote:
> Due to changes in the struct device_driver there is no direct
> access to its kobj any longer. The kobj was used to create
> sysfs links between eHEA ethernet devices and the driver.
> This patch removes the affected sysfs links to resolve
> the build problems.
> 
> Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
> 
> 
> ---
>  drivers/net/ehea/ehea_main.c |   37 -------------------------------------
>  1 files changed, 0 insertions(+), 37 deletions(-)

applied

^ permalink raw reply

* Re: [PATCH] [POWERPC] Xilinx: hwicap driver
From: Grant Likely @ 2008-02-01 18:42 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: linuxppc-dev, ntl
In-Reply-To: <20080201182253.903B5F18046@mail130-sin.bigfish.com>

On 2/1/08, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
> diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
> new file mode 100644
> index 0000000..83f2d0b
> --- /dev/null
> +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
> +static int hwicap_open(struct inode *inode, struct file *file)
> +{
> +       struct hwicap_drvdata *drvdata;
> +       int status;
> +
> +       drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev);
> +
> +       if (down_interruptible(&drvdata->sem))
> +               return -ERESTARTSYS;
> +
> +       drvdata->is_open = 1;

It doesn't look like this flag is used anywhere anymore.

> +
> +       status = hwicap_initialize_hwicap(drvdata);
> +       if (status) {
> +               dev_err(drvdata->dev, "Failed to open file");
> +               return status;

returning without up()

> +       }
> +
> +       file->private_data = drvdata;
> +       drvdata->write_buffer_in_use = 0;
> +       drvdata->read_buffer_in_use = 0;
> +
> +       up(&drvdata->sem);
> +
> +       return 0;
> +}
> +

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

^ permalink raw reply

* Re: [PATCH 1/5] ehea: fix ehea.h checkpatch complaints
From: Jeff Garzik @ 2008-02-01 18:43 UTC (permalink / raw)
  To: netdev; +Cc: Linux PowerPC List, Paul Mackerras, Jan-Bernd Themann
In-Reply-To: <47A36798.8040208@garzik.org>

Jeff Garzik wrote:
> Doug Maxey wrote:
>> Cc: Jan-Bernd Themann <themann@de.ibm.com>
>> Signed-off-by: Doug Maxey <dwm@austin.ibm.com>
>> ---
>>  drivers/net/ehea/ehea.h    |    3 +++
>>  drivers/net/ehea/ehea_hw.h |    8 ++++----
>>  2 files changed, 7 insertions(+), 4 deletions(-)
> 
> applied 1-5


BTW please do not reply to jgarzik@dunveganmedia.com.

That was a Thunderbird fsck-up on my side.

^ permalink raw reply

* 8360 custom board, ucc_geth TX errors on longer(?) packets
From: Steven Hein @ 2008-02-01 18:52 UTC (permalink / raw)
  To: linuxppc-embedded

Hi All:

For our custom 8360-based board that is based fairly closely
on the MPC8360-MDS board, I'm working on moving from a 2.6.16
kernel (Timesys distro) to 2.6.24.   I didn't use the device
tree in my old kernel, so I've had the learning experience
of tweaking the dts file for my custom board.  I've got it 99%
working  (I think!).   But I'm running into a really odd ethernet
problem that I don't understand.

I have two boards:  our custom 8360 board, and a MPC8360E-MDS
dev board.    I can build a kernel for the MDS board and
ethernet works perfectly.    When I build that kernel using the
same config, but changing the platform to my custom board
(and not changing any other options), everything works right
except for ethernet:    it won't get an IP address using DHCP
(udhcpc)...    I check the ifconfig output, and I see that
the TX error count is increasing, and there are very few TX
packets being sent.   I verified that this isn't a timeout--
it's being caused by the UCCE[TXE] bit being set   (I don't see
any other error indications in dmesg).

As another test, I configured the address manually.   When I do
that, I can ping the board with a normal ping; but if I use
the -s option to increase the packet size, then the pings will
fail when I use a size of about 250 or greater, and with a size
of about 300 or greater I get no successful ping packets.

The one main difference in this board is how eth0 is wired.
We have a Broadcom GbE switch part, and UCC1 eth is wired
directly to that switch (no PHY).   (This where I needed to
use the fixed-link option that I had asked about a couple of
days ago!).   In my 2.6.16-based kernel, I had created a
dummy "null" PHY to force the link speed to 1000, and that
worked well.    I am using the same Broadcom network
switch software with both kernels, so the switch side of the
config should be the same.

I created my custom board config from the MPC8360E-MDS board
configs (starting with that defconfig, dts, and platform .c
file), and matched my GPIO pin configs in the dtb file to what
I had used in the 2.6.16 kernel......  I'm not sure where to
look for this problem, as I've never seen UCCE[TXE] set
before.    Based on my experience in this move so far, I'd
guess that I've got something configured wrong somewhere,
but I just don't know where to look.

Does anyone have any ideas/suggestions?
Any help would be MUCH appreciated!!

Thanks,
Steve

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Steve Hein (ssh@sgi.com)              Engineering Diagnostics/Software
Silicon Graphics, Inc.                          
1168 Industrial Blvd.                 Phone: (715) 726-8410
Chippewa Falls, WI 54729              Fax:   (715) 726-6715
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

^ permalink raw reply

* Re: [PATCH 4/5] ehea: fix phyp checkpatch complaints
From: Scott Wood @ 2008-02-01 19:23 UTC (permalink / raw)
  To: Doug Maxey
  Cc: Linux PowerPC List, Jan-Bernd Themann, Jeff Garzik,
	Paul Mackerras, netdev
In-Reply-To: <1201832451-23634-5-git-send-email-dwm@austin.ibm.com>

On Thu, Jan 31, 2008 at 08:20:50PM -0600, Doug Maxey wrote:
>  /* input param R5 */
> -#define H_ALL_RES_QP_EQPO         EHEA_BMASK_IBM(9, 11)
> -#define H_ALL_RES_QP_QPP          EHEA_BMASK_IBM(12, 12)
> -#define H_ALL_RES_QP_RQR          EHEA_BMASK_IBM(13, 15)
> -#define H_ALL_RES_QP_EQEG         EHEA_BMASK_IBM(16, 16)
> -#define H_ALL_RES_QP_LL_QP        EHEA_BMASK_IBM(17, 17)
> -#define H_ALL_RES_QP_DMA128       EHEA_BMASK_IBM(19, 19)
> -#define H_ALL_RES_QP_HSM          EHEA_BMASK_IBM(20, 21)
> -#define H_ALL_RES_QP_SIGT         EHEA_BMASK_IBM(22, 23)
> -#define H_ALL_RES_QP_TENURE       EHEA_BMASK_IBM(48, 55)
> -#define H_ALL_RES_QP_RES_TYP      EHEA_BMASK_IBM(56, 63)
> +#define H_ALL_RES_QP_EQPO	  EHEA_BMASK_IBM(9, 11)
> +#define H_ALL_RES_QP_QPP	  EHEA_BMASK_IBM(12, 12)
> +#define H_ALL_RES_QP_RQR	  EHEA_BMASK_IBM(13, 15)
> +#define H_ALL_RES_QP_EQEG	  EHEA_BMASK_IBM(16, 16)
> +#define H_ALL_RES_QP_LL_QP	  EHEA_BMASK_IBM(17, 17)
> +#define H_ALL_RES_QP_DMA128	  EHEA_BMASK_IBM(19, 19)
> +#define H_ALL_RES_QP_HSM	  EHEA_BMASK_IBM(20, 21)
> +#define H_ALL_RES_QP_SIGT	  EHEA_BMASK_IBM(22, 23)
> +#define H_ALL_RES_QP_TENURE	  EHEA_BMASK_IBM(48, 55)
> +#define H_ALL_RES_QP_RES_TYP	  EHEA_BMASK_IBM(56, 63)

This was better the way it was (before, it was readable at any tab setting);
checkpatch is overeager to complain on tab/space issues (it's a bit hard to
distinguish indentation from alignment with a regex).

-Scott

^ permalink raw reply

* RE: [RFC/PATCH v2] [POWERPC] bootwrapper: build multiple cuImages
From: Stephen Neuendorffer @ 2008-02-01 19:32 UTC (permalink / raw)
  To: Grant Likely, jwboyer, linuxppc-dev, scotwood, galak, jochen,
	geoffrey.levand
In-Reply-To: <20080201000706.18869.36903.stgit@trillian.secretlab.ca>

What if it was something like:

image-$(CONFIG_MPC832x_MDS)		+=3D cuImage.mpc832x_mds
image-$(CONFIG_MPC832x_RDB)		+=3D cuImage.mpc832x_rdb
image-$(CONFIG_MPC834x_ITX)		+=3D cuImage.mpc8349emitx \
					   cuImage.mpc8349emitxgp
platform-$(CONFIG_MPC83xx)		+=3D cuboot-83xx.o

$(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) $(platform-y)
	$(call if_changed,wrap,$(platform-y),(dtstree)/$*.dts)

Which would eliminate making all the unnecessary bits of platform code
and enabling the smart bits to be factored out of the wrapper?  It would
also make the dependency between the board and the platform code
specific.It would also prevent building more than one platform at a
time, but I don't think that's really possibly anyway?

Steve

^ permalink raw reply

* Re: External Interrupt
From: Scott Wood @ 2008-02-01 19:43 UTC (permalink / raw)
  To: Marco Stornelli; +Cc: Linuxppc Embedded Mailing List
In-Reply-To: <47A3473D.6040505@coritel.it>

Marco Stornelli wrote:
> Yes you are right. The local bus is like i2c, but I've never seen a
> device connected with i2c and described with a sub-node of i2c node in
> the dts file,

Grep the dts directory for "rtc@68".

-Scott

^ permalink raw reply

* [RFC/PATCH] powerpc: Add irqtrace support to 32-bit powerpc
From: Dale Farnsworth @ 2008-02-01 19:50 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Johannes Berg

This adds the low level irq tracing hooks for 32-bit powerpc.
This patch applies on top of Benjamin Herrenschmidt's patch that
provides similar support for 64-bit powerpc.  See
<http://ozlabs.org/pipermail/linuxppc-dev/2007-October/044361.html>

It seems to work on my prpmc2800 board with a ppc7447.

Dale Farnsworth <dale@farnsworth.org>
---

 arch/powerpc/Kconfig           |    1 -
 arch/powerpc/kernel/entry_32.S |   80 ++++++++++++++++++++++++++++-
 arch/powerpc/kernel/setup_32.c |    2 +
 include/asm-powerpc/hw_irq.h   |   20 ++++----
 include/asm-powerpc/system.h   |    3 +-
 5 files changed, 90 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c313e68..933e98b 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -52,7 +52,6 @@ config STACKTRACE_SUPPORT
 
 config TRACE_IRQFLAGS_SUPPORT
 	bool
-	depends on PPC64
 	default y
 
 config LOCKDEP_SUPPORT
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 69a91bd..f7d2c75 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -144,6 +144,37 @@ transfer_to_handler:
 	.globl transfer_to_handler_cont
 transfer_to_handler_cont:
 3:
+#ifdef CONFIG_TRACE_IRQFLAGS
+	lis	r11,reenable_mmu@h
+	ori	r11,r11,reenable_mmu@l
+	mtspr	SPRN_SRR0,r11
+	mtspr	SPRN_SRR1,r10
+	SYNC
+	RFI
+reenable_mmu:				/* re-enable mmu so we can */
+	mflr	r9			/* call C code, if necessary */
+	mfmsr	r10
+	lwz	r11,_MSR(r1)
+	xor	r10,r10,r11
+	andi.	r10,r10,MSR_EE
+	beq	1f
+	stwu	r1,-16(r1)
+	stw	r9,20(r1)
+	stw	r3,12(r1)
+	bl      trace_hardirqs_off
+	lwz	r3,12(r1)
+	lwz	r9,20(r1)
+	addi	r1,r1,16
+	REST_GPR(4,r1)
+	REST_4GPRS(5,r1)
+1:
+	tovirt(r9,r9)
+	lwz	r11,0(r9)		/* virtual address of handler */
+	lwz	r9,4(r9)		/* where to go when done */
+	mtctr	r11
+	mtlr	r9
+	bctr				/* jump to handler */
+#else /* CONFIG_TRACE_IRQFLAGS */
 	mflr	r9
 	lwz	r11,0(r9)		/* virtual address of handler */
 	lwz	r9,4(r9)		/* where to go when done */
@@ -152,6 +183,7 @@ transfer_to_handler_cont:
 	mtlr	r9
 	SYNC
 	RFI				/* jump to handler, enable MMU */
+#endif /* CONFIG_TRACE_IRQFLAGS */
 
 #ifdef CONFIG_6xx
 4:	rlwinm	r12,r12,0,~_TLF_NAPPING
@@ -220,12 +252,20 @@ ret_from_syscall:
 #ifdef SHOW_SYSCALLS
 	bl	do_show_syscall_exit
 #endif
-	mr	r6,r3
 	rlwinm	r12,r1,0,0,(31-THREAD_SHIFT)	/* current_thread_info() */
 	/* disable interrupts so current_thread_info()->flags can't change */
 	LOAD_MSR_KERNEL(r10,MSR_KERNEL)	/* doesn't include MSR_EE */
 	SYNC
 	MTMSRD(r10)
+#ifdef CONFIG_TRACE_IRQFLAGS
+	stwu	r1,-16(r1)
+	stw	r3,12(r1)
+	bl      trace_hardirqs_off
+	lwz	r3,12(r1)
+	addi	r1,r1,16
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+#endif
+	mr	r6,r3
 	lwz	r9,TI_FLAGS(r12)
 	li	r8,-_LAST_ERRNO
 	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
@@ -237,6 +277,13 @@ ret_from_syscall:
 	oris	r11,r11,0x1000	/* Set SO bit in CR */
 	stw	r11,_CCR(r1)
 syscall_exit_cont:
+#ifdef CONFIG_TRACE_IRQFLAGS
+	stwu	r1,-16(r1)
+	stw	r3,12(r1)
+	bl      trace_hardirqs_on
+	lwz	r3,12(r1)
+	addi	r1,r1,16
+#endif
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
 	/* If the process has its own DBCR0 value, load it up.  The single
 	   step bit tells us that dbcr0 should be loaded. */
@@ -337,7 +384,10 @@ syscall_exit_work:
 4:	/* Anything which requires enabling interrupts? */
 	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
 	beq	ret_from_except
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bl      trace_hardirqs_on
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+#endif
 	/* Re-enable interrupts */
 	ori	r10,r10,MSR_EE
 	SYNC
@@ -646,13 +696,22 @@ ret_from_except_full:
 
 	.globl	ret_from_except
 ret_from_except:
+#ifdef CONFIG_TRACE_IRQFLAGS
+	mfmsr	r3
+#endif
 	/* Hard-disable interrupts so that current_thread_info()->flags
 	 * can't change between when we test it and when we return
 	 * from the interrupt. */
 	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
 	SYNC			/* Some chip revs have problems here... */
 	MTMSRD(r10)		/* disable interrupts */
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	andi.	r3,r3,MSR_EE
+	beq	1f			/* verified */
+	bl	trace_hardirqs_off
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+1:
+#endif
 	lwz	r3,_MSR(r1)	/* Returning to user mode? */
 	andi.	r0,r3,MSR_PR
 	beq	resume_kernel
@@ -709,6 +768,9 @@ restore:
 	stw	r6,icache_44x_need_flush@l(r4)
 1:
 #endif  /* CONFIG_44x */
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bl	trace_hardirqs_on
+#endif
 	lwz	r0,GPR0(r1)
 	lwz	r2,GPR2(r1)
 	REST_4GPRS(3, r1)
@@ -900,6 +962,10 @@ do_work:			/* r10 contains MSR_KERNEL here */
 	beq	do_user_signal
 
 do_resched:			/* r10 contains MSR_KERNEL here */
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bl	trace_hardirqs_on
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+#endif
 	ori	r10,r10,MSR_EE
 	SYNC
 	MTMSRD(r10)		/* hard-enable interrupts */
@@ -908,6 +974,10 @@ recheck:
 	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
 	SYNC
 	MTMSRD(r10)		/* disable interrupts */
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bl	trace_hardirqs_off
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+#endif
 	rlwinm	r9,r1,0,0,(31-THREAD_SHIFT)
 	lwz	r9,TI_FLAGS(r9)
 	andi.	r0,r9,_TIF_NEED_RESCHED
@@ -915,6 +985,10 @@ recheck:
 	andi.	r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK
 	beq	restore_user
 do_user_signal:			/* r10 contains MSR_KERNEL here */
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bl	trace_hardirqs_on
+	LOAD_MSR_KERNEL(r10,MSR_KERNEL)
+#endif
 	ori	r10,r10,MSR_EE
 	SYNC
 	MTMSRD(r10)		/* hard-enable interrupts */
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index cd870a8..725dd18 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -120,6 +120,8 @@ unsigned long __init early_init(unsigned long dt_ptr)
  */
 void __init machine_init(unsigned long dt_ptr, unsigned long phys)
 {
+	lockdep_init();
+
 	/* Enable early debugging if any specified (see udbg.h) */
 	udbg_early_init();
 
diff --git a/include/asm-powerpc/hw_irq.h b/include/asm-powerpc/hw_irq.h
index ad8c9f7..a5f347a 100644
--- a/include/asm-powerpc/hw_irq.h
+++ b/include/asm-powerpc/hw_irq.h
@@ -63,13 +63,13 @@ extern void iseries_handle_interrupts(void);
 
 #if defined(CONFIG_BOOKE)
 #define SET_MSR_EE(x)	mtmsr(x)
-#define local_irq_restore(flags)	__asm__ __volatile__("wrtee %0" : : "r" (flags) : "memory")
+#define raw_local_irq_restore(flags)	__asm__ __volatile__("wrtee %0" : : "r" (flags) : "memory")
 #else
 #define SET_MSR_EE(x)	mtmsr(x)
-#define local_irq_restore(flags)	mtmsr(flags)
+#define raw_local_irq_restore(flags)	mtmsr(flags)
 #endif
 
-static inline void local_irq_disable(void)
+static inline void raw_local_irq_disable(void)
 {
 #ifdef CONFIG_BOOKE
 	__asm__ __volatile__("wrteei 0": : :"memory");
@@ -81,7 +81,7 @@ static inline void local_irq_disable(void)
 #endif
 }
 
-static inline void local_irq_enable(void)
+static inline void raw_local_irq_enable(void)
 {
 #ifdef CONFIG_BOOKE
 	__asm__ __volatile__("wrteei 1": : :"memory");
@@ -93,7 +93,7 @@ static inline void local_irq_enable(void)
 #endif
 }
 
-static inline void local_irq_save_ptr(unsigned long *flags)
+static inline void raw_local_irq_save_ptr(unsigned long *flags)
 {
 	unsigned long msr;
 	msr = mfmsr();
@@ -106,12 +106,12 @@ static inline void local_irq_save_ptr(unsigned long *flags)
 	__asm__ __volatile__("": : :"memory");
 }
 
-#define local_save_flags(flags)	((flags) = mfmsr())
-#define local_irq_save(flags)	local_irq_save_ptr(&flags)
-#define irqs_disabled()		((mfmsr() & MSR_EE) == 0)
+#define raw_local_save_flags(flags)	((flags) = mfmsr())
+#define raw_local_irq_save(flags)	raw_local_irq_save_ptr(&flags)
+#define raw_irqs_disabled()		((mfmsr() & MSR_EE) == 0)
+#define raw_irqs_disabled_flags(flags)	(((flags) & MSR_EE) == 0)
 
-#define hard_irq_enable()	local_irq_enable()
-#define hard_irq_disable()	local_irq_disable()
+#define hard_irq_disable()		raw_local_irq_disable()
 
 #endif /* CONFIG_PPC64 */
 
diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h
index 87be8c3..b8024b9 100644
--- a/include/asm-powerpc/system.h
+++ b/include/asm-powerpc/system.h
@@ -5,8 +5,7 @@
 #define _ASM_POWERPC_SYSTEM_H
 
 #include <linux/kernel.h>
-
-#include <asm/hw_irq.h>
+#include <linux/irqflags.h>
 
 /*
  * Memory barrier.
-- 
1.5.3.4

^ permalink raw reply related

* Re: 8360 custom board, ucc_geth TX errors on longer(?) packets
From: Kim Phillips @ 2008-02-01 19:53 UTC (permalink / raw)
  To: Steven Hein; +Cc: linuxppc-embedded
In-Reply-To: <47A36A69.1010809@sgi.com>

On Fri, 01 Feb 2008 12:52:25 -0600
Steven Hein <ssh@sgi.com> wrote:

> The one main difference in this board is how eth0 is wired.
> We have a Broadcom GbE switch part, and UCC1 eth is wired
> directly to that switch (no PHY).   (This where I needed to

sounds like you ran into some h/w errata.  if on rgmii, you might
want to find a way to program the switch for rgmii with internal delay
(8360 rev.2 rgmii-id rx & tx; 8360rev2.1 rgmii-rxid (i.e. for rx
only)).  If not, I'd contact fsl tech support directly.

Kim

^ permalink raw reply

* Re: External Interrupt
From: Jon Loeliger @ 2008-02-01 19:55 UTC (permalink / raw)
  To: Scott Wood; +Cc: Marco Stornelli, Linuxppc Embedded Mailing List
In-Reply-To: <47A37653.3070305@freescale.com>

On Fri, 2008-02-01 at 13:43, Scott Wood wrote:
> Marco Stornelli wrote:
> > Yes you are right. The local bus is like i2c, but I've never seen a
> > device connected with i2c and described with a sub-node of i2c node in
> > the dts file,
> 
> Grep the dts directory for "rtc@68".

But be sure to inspect /dts-v1/-ness too.

Yes, yes, yes. I have a typo fix coming... :-)

jdl

^ permalink raw reply

* Re: [RFC/PATCH v2] [POWERPC] bootwrapper: build multiple cuImages
From: Grant Likely @ 2008-02-01 19:57 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: scotwood, linuxppc-dev
In-Reply-To: <20080201193225.67CFAAF0057@mail201-sin.bigfish.com>

On 2/1/08, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
> What if it was something like:
>
> image-$(CONFIG_MPC832x_MDS)             += cuImage.mpc832x_mds
> image-$(CONFIG_MPC832x_RDB)             += cuImage.mpc832x_rdb
> image-$(CONFIG_MPC834x_ITX)             += cuImage.mpc8349emitx \
>                                            cuImage.mpc8349emitxgp
> platform-$(CONFIG_MPC83xx)              += cuboot-83xx.o
>
> $(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) $(platform-y)
>         $(call if_changed,wrap,$(platform-y),(dtstree)/$*.dts)
>
> Which would eliminate making all the unnecessary bits of platform code
> and enabling the smart bits to be factored out of the wrapper?  It would
> also make the dependency between the board and the platform code
> specific.It would also prevent building more than one platform at a
> time, but I don't think that's really possibly anyway?

It *is* possible, which is the goal of this patch.

:-D

There are only a few bits of non-multiplatform stuff left for 6xx
compatible platforms and those won't take too long to clear out.

The biggest part of the problem is the overloading of the cuImage.%
target.  If the wrapper was encoded in the target then the problem
would go away.  For example:

image-$(CONFIG_LITE5200)             += cuImage-52xx.lite5200b
image-$(CONFIG_MPC832x_MDS)             += cuImage-83xx.mpc832x_mds
image-$(CONFIG_MPC832x_RDB)             += cuImage-83xx.mpc832x_rdb
image-$(CONFIG_MPC834x_ITX)             += cuImage-83xx.mpc8349emitx \
                                           cuImage-83xx.mpc8349emitxgp

And then have targets for each of the cuImage-<platform>.%, like so:

$(obj)/cuImage-52xx-%: vmlinux $(dtstree)/%.dts $(wrapperbits)
        $(call if_changed,wrap,cuboot-52xx,(dtstree)/$*.dts)
$(obj)/cuImage-83xx-%: vmlinux $(dtstree)/%.dts $(wrapperbits)
        $(call if_changed,wrap,cuboot-83xx,(dtstree)/$*.dts)

Cheers,
g.

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

^ permalink raw reply

* [PATCH] [POWERPC] Xilinx: hwicap driver
From: Stephen Neuendorffer @ 2008-02-01 20:03 UTC (permalink / raw)
  To: linuxppc-dev, grant.likely, jacmet, ntl
In-Reply-To: <1201890163-12219-1-git-send-email-stephen.neuendorffer@xilinx.com>

This includes code for new fifo-based xps_hwicap in addition to the
older opb_hwicap, which has a significantly different interface.  The
common code between the two drivers is largely shared.

Significant differences exists between this driver and what is
supported in the EDK drivers.  In particular, most of the
architecture-specific code for reconfiguring individual FPGA resources
has been removed.  This functionality is likely better provided in a
user-space support library.  In addition, read and write access is
supported.  In addition, although the xps_hwicap cores support
interrupt-driver mode, this driver only supports polled operation, in
order to make the code simpler, and since the interrupt processing
overhead is likely to slow down the throughput under Linux.

Signed-off-by: Stephen Neuendorffer <stephen.neuendorffer@xilinx.com>

Fixed to add mutexes, and a few style issues.

---

I will get this right eventually. :)
---
 drivers/char/Kconfig                       |    7 +
 drivers/char/Makefile                      |    1 +
 drivers/char/xilinx_hwicap/Makefile        |    7 +
 drivers/char/xilinx_hwicap/buffer_icap.c   |  380 ++++++++++++
 drivers/char/xilinx_hwicap/buffer_icap.h   |   57 ++
 drivers/char/xilinx_hwicap/fifo_icap.c     |  381 ++++++++++++
 drivers/char/xilinx_hwicap/fifo_icap.h     |   62 ++
 drivers/char/xilinx_hwicap/xilinx_hwicap.c |  904 ++++++++++++++++++++++++++++
 drivers/char/xilinx_hwicap/xilinx_hwicap.h |  193 ++++++
 9 files changed, 1992 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/xilinx_hwicap/Makefile
 create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.c
 create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.h
 create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.c
 create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.h
 create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.c
 create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ef1ed5d..157ae2a 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -831,6 +831,13 @@ config DTLK
 	  To compile this driver as a module, choose M here: the
 	  module will be called dtlk.
 
+config XILINX_HWICAP
+	tristate "Xilinx HWICAP Support"
+	depends on XILINX_VIRTEX
+	help
+	  This option enables support for Xilinx Internal Configuration
+	  Access Port (ICAP) driver.
+
 config R3964
 	tristate "Siemens R3964 line discipline"
 	---help---
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 07304d5..3a278a0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_EFI_RTC)		+= efirtc.o
 obj-$(CONFIG_SGI_DS1286)	+= ds1286.o
 obj-$(CONFIG_SGI_IP27_RTC)	+= ip27-rtc.o
 obj-$(CONFIG_DS1302)		+= ds1302.o
+obj-$(CONFIG_XILINX_HWICAP)    += xilinx_hwicap/
 ifeq ($(CONFIG_GENERIC_NVRAM),y)
   obj-$(CONFIG_NVRAM)	+= generic_nvram.o
 else
diff --git a/drivers/char/xilinx_hwicap/Makefile b/drivers/char/xilinx_hwicap/Makefile
new file mode 100644
index 0000000..5491cbc
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Xilinx OPB hwicap driver
+#
+
+obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap_m.o 
+ 
+xilinx_hwicap_m-y := xilinx_hwicap.o fifo_icap.o buffer_icap.o
diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c
new file mode 100644
index 0000000..dfea2bd
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/buffer_icap.c
@@ -0,0 +1,380 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 "buffer_icap.h"
+
+/* Indicates how many bytes will fit in a buffer. (1 BRAM) */
+#define XHI_MAX_BUFFER_BYTES        2048
+#define XHI_MAX_BUFFER_INTS         (XHI_MAX_BUFFER_BYTES >> 2)
+
+/* File access and error constants */
+#define XHI_DEVICE_READ_ERROR       -1
+#define XHI_DEVICE_WRITE_ERROR      -2
+#define XHI_BUFFER_OVERFLOW_ERROR   -3
+
+#define XHI_DEVICE_READ             0x1
+#define XHI_DEVICE_WRITE            0x0
+
+/* Constants for checking transfer status */
+#define XHI_CYCLE_DONE              0
+#define XHI_CYCLE_EXECUTING         1
+
+/* buffer_icap register offsets */
+
+/* Size of transfer, read & write */
+#define XHI_SIZE_REG_OFFSET        0x800L
+/* offset into bram, read & write */
+#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L
+/* Read not Configure, direction of transfer.  Write only */
+#define XHI_RNC_REG_OFFSET         0x808L
+/* Indicates transfer complete. Read only */
+#define XHI_STATUS_REG_OFFSET      0x80CL
+
+/* Constants for setting the RNC register */
+#define XHI_CONFIGURE              0x0UL
+#define XHI_READBACK               0x1UL
+
+/* Constants for the Done register */
+#define XHI_NOT_FINISHED           0x0UL
+#define XHI_FINISHED               0x1UL
+
+#define XHI_BUFFER_START 0
+
+/**
+ * buffer_icap_get_status: Get the contents of the status register.
+ * @parameter base_address: is the base address of the device
+ *
+ * The status register contains the ICAP status and the done bit.
+ *
+ * D8 - cfgerr
+ * D7 - dalign
+ * D6 - rip
+ * D5 - in_abort_l
+ * D4 - Always 1
+ * D3 - Always 1
+ * D2 - Always 1
+ * D1 - Always 1
+ * D0 - Done bit
+ **/
+static inline u32 buffer_icap_get_status(void __iomem *base_address)
+{
+	return in_be32(base_address + XHI_STATUS_REG_OFFSET);
+}
+
+/**
+ * buffer_icap_get_bram: Reads data from the storage buffer bram.
+ * @parameter base_address: contains the base address of the component.
+ * @parameter offset: The word offset from which the data should be read.
+ *
+ * A bram is used as a configuration memory cache.  One frame of data can
+ * be stored in this "storage buffer".
+ **/
+static inline u32 buffer_icap_get_bram(void __iomem *base_address,
+		u32 offset)
+{
+	return in_be32(base_address + (offset << 2));
+}
+
+/**
+ * buffer_icap_busy: Return true if the icap device is busy
+ * @parameter base_address: is the base address of the device
+ *
+ * The queries the low order bit of the status register, which
+ * indicates whether the current configuration or readback operation
+ * has completed.
+ **/
+static inline bool buffer_icap_busy(void __iomem *base_address)
+{
+	return (buffer_icap_get_status(base_address) & 1) == XHI_NOT_FINISHED;
+}
+
+/**
+ * buffer_icap_busy: Return true if the icap device is not busy
+ * @parameter base_address: is the base address of the device
+ *
+ * The queries the low order bit of the status register, which
+ * indicates whether the current configuration or readback operation
+ * has completed.
+ **/
+static inline bool buffer_icap_done(void __iomem *base_address)
+{
+	return (buffer_icap_get_status(base_address) & 1) == XHI_FINISHED;
+}
+
+/**
+ * buffer_icap_set_size: Set the size register.
+ * @parameter base_address: is the base address of the device
+ * @parameter data: The size in bytes.
+ *
+ * The size register holds the number of 8 bit bytes to transfer between
+ * bram and the icap (or icap to bram).
+ **/
+static inline void buffer_icap_set_size(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_SIZE_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_mSetoffsetReg: Set the bram offset register.
+ * @parameter base_address: contains the base address of the device.
+ * @parameter data: is the value to be written to the data register.
+ *
+ * The bram offset register holds the starting bram address to transfer
+ * data from during configuration or write data to during readback.
+ **/
+static inline void buffer_icap_set_offset(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_set_rnc: Set the RNC (Readback not Configure) register.
+ * @parameter base_address: contains the base address of the device.
+ * @parameter data: is the value to be written to the data register.
+ *
+ * The RNC register determines the direction of the data transfer.  It
+ * controls whether a configuration or readback take place.  Writing to
+ * this register initiates the transfer.  A value of 1 initiates a
+ * readback while writing a value of 0 initiates a configuration.
+ **/
+static inline void buffer_icap_set_rnc(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_RNC_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_set_bram: Write data to the storage buffer bram.
+ * @parameter base_address: contains the base address of the component.
+ * @parameter offset: The word offset at which the data should be written.
+ * @parameter data: The value to be written to the bram offset.
+ *
+ * A bram is used as a configuration memory cache.  One frame of data can
+ * be stored in this "storage buffer".
+ **/
+static inline void buffer_icap_set_bram(void __iomem *base_address,
+		u32 offset, u32 data)
+{
+	out_be32(base_address + (offset << 2), data);
+}
+
+/**
+ * buffer_icap_device_read: Transfer bytes from ICAP to the storage buffer.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter offset: The storage buffer start address.
+ * @parameter count: The number of words (32 bit) to read from the
+ *           device (ICAP).
+ **/
+static int buffer_icap_device_read(struct hwicap_drvdata *drvdata,
+		u32 offset, u32 count)
+{
+
+	s32 retries = 0;
+	void __iomem *base_address = drvdata->base_address;
+
+	if (buffer_icap_busy(base_address))
+		return -EBUSY;
+
+	if ((offset + count) > XHI_MAX_BUFFER_INTS)
+		return -EINVAL;
+
+	/* setSize count*4 to get bytes. */
+	buffer_icap_set_size(base_address, (count << 2));
+	buffer_icap_set_offset(base_address, offset);
+	buffer_icap_set_rnc(base_address, XHI_READBACK);
+
+	while (buffer_icap_busy(base_address)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			return -EBUSY;
+	}
+	return 0;
+
+};
+
+/**
+ * buffer_icap_device_write: Transfer bytes from ICAP to the storage buffer.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter offset: The storage buffer start address.
+ * @parameter count: The number of words (32 bit) to read from the
+ *           device (ICAP).
+ **/
+static int buffer_icap_device_write(struct hwicap_drvdata *drvdata,
+		u32 offset, u32 count)
+{
+
+	s32 retries = 0;
+	void __iomem *base_address = drvdata->base_address;
+
+	if (buffer_icap_busy(base_address))
+		return -EBUSY;
+
+	if ((offset + count) > XHI_MAX_BUFFER_INTS)
+		return -EINVAL;
+
+	/* setSize count*4 to get bytes. */
+	buffer_icap_set_size(base_address, count << 2);
+	buffer_icap_set_offset(base_address, offset);
+	buffer_icap_set_rnc(base_address, XHI_CONFIGURE);
+
+	while (buffer_icap_busy(base_address)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			return -EBUSY;
+	}
+	return 0;
+
+};
+
+/**
+ * buffer_icap_reset: Reset the logic of the icap device.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Writing to the status register resets the ICAP logic in an internal
+ * version of the core.  For the version of the core published in EDK,
+ * this is a noop.
+ **/
+void buffer_icap_reset(struct hwicap_drvdata *drvdata)
+{
+    out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE);
+}
+
+/**
+ * buffer_icap_set_configuration: Load a partial bitstream from system memory.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Kernel address of the partial bitstream.
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ **/
+int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 size)
+{
+	int status;
+	s32 buffer_count = 0;
+	s32 num_writes = 0;
+	bool dirty = 0;
+	u32 i;
+	void __iomem *base_address = drvdata->base_address;
+
+	/* Loop through all the data */
+	for (i = 0, buffer_count = 0; i < size; i++) {
+
+		/* Copy data to bram */
+		buffer_icap_set_bram(base_address, buffer_count, data[i]);
+		dirty = 1;
+
+		if (buffer_count < XHI_MAX_BUFFER_INTS - 1) {
+			buffer_count++;
+			continue;
+		}
+
+		/* Write data to ICAP */
+		status = buffer_icap_device_write(
+				drvdata,
+				XHI_BUFFER_START,
+				XHI_MAX_BUFFER_INTS);
+		if (status != 0) {
+			/* abort. */
+			buffer_icap_reset(drvdata);
+			return status;
+		}
+
+		buffer_count = 0;
+		num_writes++;
+		dirty = 0;
+	}
+
+	/* Write unwritten data to ICAP */
+	if (dirty) {
+		/* Write data to ICAP */
+		status = buffer_icap_device_write(drvdata, XHI_BUFFER_START,
+					     buffer_count);
+		if (status != 0) {
+			/* abort. */
+			buffer_icap_reset(drvdata);
+		}
+		return status;
+	}
+
+	return 0;
+};
+
+/**
+ * buffer_icap_get_configuration: Read configuration data from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Address of the data representing the partial bitstream
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ **/
+int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 size)
+{
+	int status;
+	s32 buffer_count = 0;
+	s32 read_count = 0;
+	u32 i;
+	void __iomem *base_address = drvdata->base_address;
+
+	/* Loop through all the data */
+	for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) {
+		if (buffer_count == XHI_MAX_BUFFER_INTS) {
+			u32 words_remaining = size - i;
+			u32 words_to_read =
+				words_remaining <
+				XHI_MAX_BUFFER_INTS ? words_remaining :
+				XHI_MAX_BUFFER_INTS;
+
+			/* Read data from ICAP */
+			status = buffer_icap_device_read(
+					drvdata,
+					XHI_BUFFER_START,
+					words_to_read);
+			if (status != 0) {
+				/* abort. */
+				buffer_icap_reset(drvdata);
+				return status;
+			}
+
+			buffer_count = 0;
+			read_count++;
+		}
+
+		/* Copy data from bram */
+		data[i] = buffer_icap_get_bram(base_address, buffer_count);
+		buffer_count++;
+	}
+
+	return 0;
+};
diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h
new file mode 100644
index 0000000..0318495
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/buffer_icap.h
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_BUFFER_ICAP_H_	/* prevent circular inclusions */
+#define XILINX_BUFFER_ICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include "xilinx_hwicap.h"
+
+void buffer_icap_reset(struct hwicap_drvdata *drvdata);
+
+/* Loads a partial bitstream from system memory. */
+int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 Size);
+
+/* Loads a partial bitstream from system memory. */
+int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 Size);
+
+#endif
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c
new file mode 100644
index 0000000..0988314
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/fifo_icap.c
@@ -0,0 +1,381 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 "fifo_icap.h"
+
+/* Register offsets for the XHwIcap device. */
+#define XHI_GIER_OFFSET	0x1C  /* Device Global Interrupt Enable Reg */
+#define XHI_IPISR_OFFSET 0x20  /* Interrupt Status Register */
+#define XHI_IPIER_OFFSET 0x28  /* Interrupt Enable Register */
+#define XHI_WF_OFFSET 0x100 /* Write FIFO */
+#define XHI_RF_OFFSET 0x104 /* Read FIFO */
+#define XHI_SZ_OFFSET 0x108 /* Size Register */
+#define XHI_CR_OFFSET 0x10C /* Control Register */
+#define XHI_SR_OFFSET 0x110 /* Status Register */
+#define XHI_WFV_OFFSET 0x114 /* Write FIFO Vacancy Register */
+#define XHI_RFO_OFFSET 0x118 /* Read FIFO Occupancy Register */
+
+/* Device Global Interrupt Enable Register (GIER) bit definitions */
+
+#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */
+
+/**
+ * HwIcap Device Interrupt Status/Enable Registers
+ *
+ * Interrupt Status Register (IPISR) : This register holds the
+ * interrupt status flags for the device. These bits are toggle on
+ * write.
+ *
+ * Interrupt Enable Register (IPIER) : This register is used to enable
+ * interrupt sources for the device.
+ * Writing a '1' to a bit enables the corresponding interrupt.
+ * Writing a '0' to a bit disables the corresponding interrupt.
+ *
+ * IPISR/IPIER registers have the same bit definitions and are only defined
+ * once.
+ */
+#define XHI_IPIXR_RFULL_MASK 0x00000008 /* Read FIFO Full */
+#define XHI_IPIXR_WEMPTY_MASK 0x00000004 /* Write FIFO Empty */
+#define XHI_IPIXR_RDP_MASK 0x00000002 /* Read FIFO half full */
+#define XHI_IPIXR_WRP_MASK 0x00000001 /* Write FIFO half full */
+#define XHI_IPIXR_ALL_MASK 0x0000000F /* Mask of all interrupts */
+
+/* Control Register (CR) */
+#define XHI_CR_SW_RESET_MASK 0x00000008 /* SW Reset Mask */
+#define XHI_CR_FIFO_CLR_MASK 0x00000004 /* FIFO Clear Mask */
+#define XHI_CR_READ_MASK 0x00000002 /* Read from ICAP to FIFO */
+#define XHI_CR_WRITE_MASK 0x00000001 /* Write from FIFO to ICAP */
+
+/* Status Register (SR) */
+#define XHI_SR_CFGERR_N_MASK 0x00000100 /* Config Error Mask */
+#define XHI_SR_DALIGN_MASK 0x00000080 /* Data Alignment Mask */
+#define XHI_SR_RIP_MASK 0x00000040 /* Read back Mask */
+#define XHI_SR_IN_ABORT_N_MASK 0x00000020 /* Select Map Abort Mask */
+#define XHI_SR_DONE_MASK 0x00000001 /* Done bit Mask  */
+
+
+#define XHI_WFO_MAX_VACANCY 1024 /* Max Write FIFO Vacancy, in words */
+#define XHI_RFO_MAX_OCCUPANCY 256 /* Max Read FIFO Occupancy, in words */
+/* The maximum amount we can request from fifo_icap_get_configuration
+   at once, in bytes. */
+#define XHI_MAX_READ_TRANSACTION_WORDS 0xFFF
+
+
+/**
+ * fifo_icap_fifo_write: Write data to the write FIFO.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: the 32-bit value to be written to the FIFO.
+ *
+ * This function will silently fail if the fifo is full.
+ **/
+static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata,
+		u32 data)
+{
+	dev_dbg(drvdata->dev, "fifo_write: %x\n", data);
+	out_be32(drvdata->base_address + XHI_WF_OFFSET, data);
+}
+
+/**
+ * fifo_icap_fifo_read: Read data from the Read FIFO.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This function will silently fail if the fifo is empty.
+ **/
+static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata)
+{
+	u32 data = in_be32(drvdata->base_address + XHI_RF_OFFSET);
+	dev_dbg(drvdata->dev, "fifo_read: %x\n", data);
+	return data;
+}
+
+/**
+ * fifo_icap_set_read_size: Set the the size register.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: the size of the following read transaction, in words.
+ **/
+static inline void fifo_icap_set_read_size(struct hwicap_drvdata *drvdata,
+		u32 data)
+{
+	out_be32(drvdata->base_address + XHI_SZ_OFFSET, data);
+}
+
+/**
+ * fifo_icap_start_config: Initiate a configuration (write) to the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline void fifo_icap_start_config(struct hwicap_drvdata *drvdata)
+{
+	out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_WRITE_MASK);
+	dev_dbg(drvdata->dev, "configuration started\n");
+}
+
+/**
+ * fifo_icap_start_readback: Initiate a readback from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata)
+{
+	out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_READ_MASK);
+	dev_dbg(drvdata->dev, "readback started\n");
+}
+
+/**
+ * fifo_icap_busy: Return true if the ICAP is still processing a transaction.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata)
+{
+	u32 status = in_be32(drvdata->base_address + XHI_SR_OFFSET);
+	dev_dbg(drvdata->dev, "Getting status = %x\n", status);
+	return (status & XHI_SR_DONE_MASK) ? 0 : 1;
+}
+
+/**
+ * fifo_icap_write_fifo_vacancy: Query the write fifo available space.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Return the number of words that can be safely pushed into the write fifo.
+ **/
+static inline u32 fifo_icap_write_fifo_vacancy(
+		struct hwicap_drvdata *drvdata)
+{
+	return in_be32(drvdata->base_address + XHI_WFV_OFFSET);
+}
+
+/**
+ * fifo_icap_read_fifo_occupancy: Query the read fifo available data.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Return the number of words that can be safely read from the read fifo.
+ **/
+static inline u32 fifo_icap_read_fifo_occupancy(
+		struct hwicap_drvdata *drvdata)
+{
+	return in_be32(drvdata->base_address + XHI_RFO_OFFSET);
+}
+
+/**
+ * fifo_icap_set_configuration: Send configuration data to the ICAP.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter frame_buffer: a pointer to the data to be written to the
+ *		ICAP device.
+ * @parameter num_words: the number of words (32 bit) to write to the ICAP
+ *		device.
+
+ * This function writes the given user data to the Write FIFO in
+ * polled mode and starts the transfer of the data to
+ * the ICAP device.
+ **/
+int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata,
+		u32 *frame_buffer, u32 num_words)
+{
+
+	u32 write_fifo_vacancy = 0;
+	u32 retries = 0;
+	u32 remaining_words;
+
+	dev_dbg(drvdata->dev, "fifo_set_configuration\n");
+
+	/*
+	 * Check if the ICAP device is Busy with the last Read/Write
+	 */
+	if (fifo_icap_busy(drvdata))
+		return -EBUSY;
+
+	/*
+	 * Set up the buffer pointer and the words to be transferred.
+	 */
+	remaining_words = num_words;
+
+	while (remaining_words > 0) {
+		/*
+		 * Wait until we have some data in the fifo.
+		 */
+		while (write_fifo_vacancy == 0) {
+			write_fifo_vacancy =
+				fifo_icap_write_fifo_vacancy(drvdata);
+			retries++;
+			if (retries > XHI_MAX_RETRIES)
+				return -EIO;
+		}
+
+		/*
+		 * Write data into the Write FIFO.
+		 */
+		while ((write_fifo_vacancy != 0) &&
+				(remaining_words > 0)) {
+			fifo_icap_fifo_write(drvdata, *frame_buffer);
+
+			remaining_words--;
+			write_fifo_vacancy--;
+			frame_buffer++;
+		}
+		/* Start pushing whatever is in the FIFO into the ICAP. */
+		fifo_icap_start_config(drvdata);
+	}
+
+	/* Wait until the write has finished. */
+	while (fifo_icap_busy(drvdata)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			break;
+	}
+
+	dev_dbg(drvdata->dev, "done fifo_set_configuration\n");
+
+	/*
+	 * If the requested number of words have not been read from
+	 * the device then indicate failure.
+	 */
+	if (remaining_words != 0)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * fifo_icap_get_configuration: Read configuration data from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Address of the data representing the partial bitstream
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ *
+ * This function reads the specified number of words from the ICAP device in
+ * the polled mode.
+ */
+int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata,
+		u32 *frame_buffer, u32 num_words)
+{
+
+	u32 read_fifo_occupancy = 0;
+	u32 retries = 0;
+	u32 *data = frame_buffer;
+	u32 remaining_words;
+	u32 words_to_read;
+
+	dev_dbg(drvdata->dev, "fifo_get_configuration\n");
+
+	/*
+	 * Check if the ICAP device is Busy with the last Write/Read
+	 */
+	if (fifo_icap_busy(drvdata))
+		return -EBUSY;
+
+	remaining_words = num_words;
+
+	while (remaining_words > 0) {
+		words_to_read = remaining_words;
+		/* The hardware has a limit on the number of words
+		   that can be read at one time.  */
+		if (words_to_read > XHI_MAX_READ_TRANSACTION_WORDS)
+			words_to_read = XHI_MAX_READ_TRANSACTION_WORDS;
+
+		remaining_words -= words_to_read;
+
+		fifo_icap_set_read_size(drvdata, words_to_read);
+		fifo_icap_start_readback(drvdata);
+
+		while (words_to_read > 0) {
+			/* Wait until we have some data in the fifo. */
+			while (read_fifo_occupancy == 0) {
+				read_fifo_occupancy =
+					fifo_icap_read_fifo_occupancy(drvdata);
+				retries++;
+				if (retries > XHI_MAX_RETRIES)
+					return -EIO;
+			}
+
+			if (read_fifo_occupancy > words_to_read)
+				read_fifo_occupancy = words_to_read;
+
+			words_to_read -= read_fifo_occupancy;
+
+			/* Read the data from the Read FIFO. */
+			while (read_fifo_occupancy != 0) {
+				*data++ = fifo_icap_fifo_read(drvdata);
+				read_fifo_occupancy--;
+			}
+		}
+	}
+
+	dev_dbg(drvdata->dev, "done fifo_get_configuration\n");
+
+	return 0;
+}
+
+/**
+ * buffer_icap_reset: Reset the logic of the icap device.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This function forces the software reset of the complete HWICAP device.
+ * All the registers will return to the default value and the FIFO is also
+ * flushed as a part of this software reset.
+ */
+void fifo_icap_reset(struct hwicap_drvdata *drvdata)
+{
+	u32 reg_data;
+	/*
+	 * Reset the device by setting/clearing the RESET bit in the
+	 * Control Register.
+	 */
+	reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data | XHI_CR_SW_RESET_MASK);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data & (~XHI_CR_SW_RESET_MASK));
+
+}
+
+/**
+ * fifo_icap_flush_fifo: This function flushes the FIFOs in the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ */
+void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata)
+{
+	u32 reg_data;
+	/*
+	 * Flush the FIFO by setting/clearing the FIFO Clear bit in the
+	 * Control Register.
+	 */
+	reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data | XHI_CR_FIFO_CLR_MASK);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data & (~XHI_CR_FIFO_CLR_MASK));
+}
+
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h
new file mode 100644
index 0000000..4d3068d
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/fifo_icap.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_FIFO_ICAP_H_	/* prevent circular inclusions */
+#define XILINX_FIFO_ICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include "xilinx_hwicap.h"
+
+/* Reads integers from the device into the storage buffer. */
+int fifo_icap_get_configuration(
+		struct hwicap_drvdata *drvdata,
+		u32 *FrameBuffer,
+		u32 NumWords);
+
+/* Writes integers to the device from the storage buffer. */
+int fifo_icap_set_configuration(
+		struct hwicap_drvdata *drvdata,
+		u32 *FrameBuffer,
+		u32 NumWords);
+
+void fifo_icap_reset(struct hwicap_drvdata *drvdata);
+void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata);
+
+#endif
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
new file mode 100644
index 0000000..a93daa2
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -0,0 +1,904 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2002 Xilinx Inc., Systems Engineering Group
+ *     (c) Copyright 2004 Xilinx Inc., Systems Engineering Group
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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.
+ *
+ *****************************************************************************/
+
+/*
+ * This is the code behind /dev/xilinx_icap -- it allows a user-space
+ * application to use the Xilinx ICAP subsystem.
+ *
+ * The following operations are possible:
+ *
+ * open		open the port and initialize for access.
+ * release	release port
+ * write        Write a bitstream to the configuration processor.
+ * read         Read a data stream from the configuration processor.
+ *
+ * After being opened, the port is initialized and accessed to avoid a
+ * corrupted first read which may occur with some hardware.  The port
+ * is left in a desynched state, requiring that a synch sequence be
+ * transmitted before any valid configuration data.  A user will have
+ * exclusive access to the device while it remains open, and the state
+ * of the ICAP cannot be guaranteed after the device is closed.  Note
+ * that a complete reset of the core and the state of the ICAP cannot
+ * be performed on many versions of the cores, hence users of this
+ * device should avoid making inconsistent accesses to the device.  In
+ * particular, accessing the read interface, without first generating
+ * a write containing a readback packet can leave the ICAP in an
+ * inaccessible state.
+ *
+ * Note that in order to use the read interface, it is first necessary
+ * to write a request packet to the write interface.  i.e., it is not
+ * possible to simply readback the bitstream (or any configuration
+ * bits) from a device without specifically requesting them first.
+ * The code to craft such packets is intended to be part of the
+ * user-space application code that uses this device.  The simplest
+ * way to use this interface is simply:
+ *
+ * cp foo.bit /dev/xilinx_icap
+ *
+ * Note that unless foo.bit is an appropriately constructed partial
+ * bitstream, this has a high likelyhood of overwriting the design
+ * currently programmed in the FPGA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/sysctl.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_OF
+/* For open firmware. */
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#endif
+
+#include "xilinx_hwicap.h"
+#include "buffer_icap.h"
+#include "fifo_icap.h"
+
+#define DRIVER_NAME "xilinx_icap"
+
+#define HWICAP_REGS   (0x10000)
+
+/* dynamically allocate device number */
+static int xhwicap_major;
+static int xhwicap_minor;
+#define HWICAP_DEVICES 1
+
+module_param(xhwicap_major, int, S_IRUGO);
+module_param(xhwicap_minor, int, S_IRUGO);
+
+/* An array, which is set to true when the device is registered. */
+static bool probed_devices[HWICAP_DEVICES];
+
+static struct class *icap_class;
+
+#define UNIMPLEMENTED 0xFFFF
+
+static const struct config_registers v2_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = 11,
+	.KEY = 12,
+	.CBC = 13,
+	.IDCODE = 14,
+	.AXSS = UNIMPLEMENTED,
+	.C0R_1 = UNIMPLEMENTED,
+	.CSOB = UNIMPLEMENTED,
+	.WBSTAR = UNIMPLEMENTED,
+	.TIMER = UNIMPLEMENTED,
+	.BOOTSTS = UNIMPLEMENTED,
+	.CTL_1 = UNIMPLEMENTED,
+};
+
+static const struct config_registers v4_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = UNIMPLEMENTED,
+	.KEY = UNIMPLEMENTED,
+	.CBC = 11,
+	.IDCODE = 12,
+	.AXSS = 13,
+	.C0R_1 = UNIMPLEMENTED,
+	.CSOB = UNIMPLEMENTED,
+	.WBSTAR = UNIMPLEMENTED,
+	.TIMER = UNIMPLEMENTED,
+	.BOOTSTS = UNIMPLEMENTED,
+	.CTL_1 = UNIMPLEMENTED,
+};
+static const struct config_registers v5_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = UNIMPLEMENTED,
+	.KEY = UNIMPLEMENTED,
+	.CBC = 11,
+	.IDCODE = 12,
+	.AXSS = 13,
+	.C0R_1 = 14,
+	.CSOB = 15,
+	.WBSTAR = 16,
+	.TIMER = 17,
+	.BOOTSTS = 18,
+	.CTL_1 = 19,
+};
+
+/**
+ * hwicap_command_desync: Send a DESYNC command to the ICAP port.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This command desynchronizes the ICAP After this command, a
+ * bitstream containing a NULL packet, followed by a SYNCH packet is
+ * required before the ICAP will recognize commands.
+ */
+int hwicap_command_desync(struct hwicap_drvdata *drvdata)
+{
+	u32 buffer[4];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1;
+	buffer[index++] = XHI_CMD_DESYNCH;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data present
+	 * in the FIFO to the ICAP device.
+	 */
+	return drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+}
+
+/**
+ * hwicap_command_capture: Send a CAPTURE command to the ICAP port.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This command captures all of the flip flop states so they will be
+ * available during readback.  One can use this command instead of
+ * enabling the CAPTURE block in the design.
+ */
+int hwicap_command_capture(struct hwicap_drvdata *drvdata)
+{
+	u32 buffer[7];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_SYNC_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1;
+	buffer[index++] = XHI_CMD_GCAPTURE;
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_DUMMY_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data
+	 * present in the FIFO to the ICAP device.
+	 */
+	return drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+
+}
+
+/**
+ * hwicap_get_configuration_register: Query a configuration register.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter reg: a constant which represents the configuration
+ *		register value to be returned.
+ * 		Examples:  XHI_IDCODE, XHI_FLR.
+ * @parameter RegData: returns the value of the register.
+ *
+ * Sends a query packet to the ICAP and then receives the response.
+ * The icap is left in Synched state.
+ */
+int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata,
+		u32 reg, u32 *RegData)
+{
+	int status;
+	u32 buffer[6];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_SYNC_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = hwicap_type_1_read(reg) | 1;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data present
+	 * in the FIFO to the ICAP device.
+	 */
+	status = drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+	if (status)
+		return status;
+
+	/*
+	 * Read the configuration register
+	 */
+	status = drvdata->config->get_configuration(drvdata, RegData, 1);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata)
+{
+	int status;
+	u32 idcode;
+
+	dev_dbg(drvdata->dev, "initializing\n");
+
+	/* Abort any current transaction, to make sure we have the
+	 * ICAP in a good state. */
+	dev_dbg(drvdata->dev, "Reset...\n");
+	drvdata->config->reset(drvdata);
+
+	dev_dbg(drvdata->dev, "Desync...\n");
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		return status;
+
+	/* Attempt to read the IDCODE from ICAP.  This
+	 * may not be returned correctly, due to the design of the
+	 * hardware.
+	 */
+	dev_dbg(drvdata->dev, "Reading IDCODE...\n");
+	status = hwicap_get_configuration_register(
+			drvdata, drvdata->config_regs->IDCODE, &idcode);
+	dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode);
+	if (status)
+		return status;
+
+	dev_dbg(drvdata->dev, "Desync...\n");
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+static ssize_t
+hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	ssize_t bytes_to_read = 0;
+	u32 *kbuf;
+	u32 words;
+	u32 bytes_remaining;
+	int status;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->read_buffer_in_use) {
+		/* If there are leftover bytes in the buffer, just */
+		/* return them and don't try to read more from the */
+		/* ICAP device. */
+		bytes_to_read =
+			(count < drvdata->read_buffer_in_use) ? count :
+			drvdata->read_buffer_in_use;
+
+		/* Return the data currently in the read buffer. */
+		if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) {
+			status = -EFAULT;
+			goto error;
+		}
+		drvdata->read_buffer_in_use -= bytes_to_read;
+		memcpy(drvdata->read_buffer + bytes_to_read,
+				drvdata->read_buffer, 4 - bytes_to_read);
+	} else {
+		/* Get new data from the ICAP, and return was was requested. */
+		kbuf = (u32 *) get_zeroed_page(GFP_KERNEL);
+		if (!kbuf) {
+			status = -ENOMEM;
+			goto error;
+		}
+
+		/* The ICAP device is only able to read complete */
+		/* words.  If a number of bytes that do not correspond */
+		/* to complete words is requested, then we read enough */
+		/* words to get the required number of bytes, and then */
+		/* save the remaining bytes for the next read. */
+
+		/* Determine the number of words to read, rounding up */
+		/* if necessary. */
+		words = ((count + 3) >> 2);
+		bytes_to_read = words << 2;
+
+		if (bytes_to_read > PAGE_SIZE)
+			bytes_to_read = PAGE_SIZE;
+
+		/* Ensure we only read a complete number of words. */
+		bytes_remaining = bytes_to_read & 3;
+		bytes_to_read &= ~3;
+		words = bytes_to_read >> 2;
+
+		status = drvdata->config->get_configuration(drvdata,
+				kbuf, words);
+
+		/* If we didn't read correctly, then bail out. */
+		if (status) {
+			free_page((unsigned long)kbuf);
+			goto error;
+		}
+
+		/* If we fail to return the data to the user, then bail out. */
+		if (copy_to_user(buf, kbuf, bytes_to_read)) {
+			free_page((unsigned long)kbuf);
+			status = -EFAULT;
+			goto error;
+		}
+		memcpy(kbuf, drvdata->read_buffer, bytes_remaining);
+		drvdata->read_buffer_in_use = bytes_remaining;
+		free_page((unsigned long)kbuf);
+	}
+	status = bytes_to_read;
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static ssize_t
+hwicap_write(struct file *file, const char *buf,
+		size_t count, loff_t *ppos)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	ssize_t written = 0;
+	ssize_t left = count;
+	u32 *kbuf;
+	ssize_t len;
+	ssize_t status;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+ 	left += drvdata->write_buffer_in_use;
+
+	/* Only write multiples of 4 bytes. */
+	if (left < 4) {
+		status = 0;
+		goto error;
+	}
+
+	kbuf = (u32 *) __get_free_page(GFP_KERNEL);
+	if (!kbuf) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	while (left > 3) {
+		/* only write multiples of 4 bytes, so there might */
+		/* be as many as 3 bytes left (at the end). */
+		len = left;
+
+		if (len > PAGE_SIZE)
+			len = PAGE_SIZE;
+		len &= ~3;
+
+		if (drvdata->write_buffer_in_use) {
+			memcpy(kbuf, drvdata->write_buffer,
+					drvdata->write_buffer_in_use);
+			if (copy_from_user(
+			    (((char *)kbuf) + (drvdata->write_buffer_in_use)),
+			    buf + written,
+			    len - (drvdata->write_buffer_in_use))) {
+				free_page((unsigned long)kbuf);
+				status = -EFAULT;
+				goto error;
+			}
+		} else {
+			if (copy_from_user(kbuf, buf + written, len)) {
+				free_page((unsigned long)kbuf);
+				status = -EFAULT;
+				goto error;
+			}
+		}
+
+		status = drvdata->config->set_configuration(drvdata,
+				kbuf, len >> 2);
+
+		if (status) {
+			free_page((unsigned long)kbuf);
+			status = -EFAULT;
+			goto error;
+		}
+		if (drvdata->write_buffer_in_use) {
+			len -= drvdata->write_buffer_in_use;
+			left -= drvdata->write_buffer_in_use;
+			drvdata->write_buffer_in_use = 0;
+		}
+		written += len;
+		left -= len;
+	}
+	if ((left > 0) && (left < 4)) {
+		if (!copy_from_user(drvdata->write_buffer,
+						buf + written, left)) {
+			drvdata->write_buffer_in_use = left;
+			written += left;
+			left = 0;
+		}
+	}
+
+	free_page((unsigned long)kbuf);
+	status = written;
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static int hwicap_open(struct inode *inode, struct file *file)
+{
+	struct hwicap_drvdata *drvdata;
+	int status;
+
+	drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev);
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->is_open) {
+		status = -EBUSY;
+		goto error;
+	}
+
+	status = hwicap_initialize_hwicap(drvdata);
+	if (status) {
+		dev_err(drvdata->dev, "Failed to open file");
+		goto error;
+	}
+
+	file->private_data = drvdata;
+	drvdata->write_buffer_in_use = 0;
+	drvdata->read_buffer_in_use = 0;
+	drvdata->is_open = 1;
+
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static int hwicap_release(struct inode *inode, struct file *file)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	int i;
+	int status = 0;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->write_buffer_in_use) {
+		/* Flush write buffer. */
+		for (i = drvdata->write_buffer_in_use; i < 4; i++)
+			drvdata->write_buffer[i] = 0;
+
+		status = drvdata->config->set_configuration(drvdata,
+				(u32 *) drvdata->write_buffer, 1);
+		if (status)
+			goto error;
+	}
+
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		goto error;
+
+ error:
+	drvdata->is_open = 0;
+	up(&drvdata->sem);
+	return status;
+}
+
+static struct file_operations hwicap_fops = {
+	.owner = THIS_MODULE,
+	.write = hwicap_write,
+	.read = hwicap_read,
+	.open = hwicap_open,
+	.release = hwicap_release,
+};
+
+static int __devinit hwicap_setup(struct device *dev, int id,
+		const struct resource *regs_res,
+		const struct hwicap_driver_config *config,
+		const struct config_registers *config_regs)
+{
+	dev_t devt;
+	struct hwicap_drvdata *drvdata = NULL;
+	int retval = 0;
+
+	dev_info(dev, "Xilinx icap port driver\n");
+
+	if (id < 0) {
+		for (id = 0; id < HWICAP_DEVICES; id++)
+			if (!probed_devices[id])
+				break;
+	}
+	if (id < 0 || id >= HWICAP_DEVICES) {
+		dev_err(dev, "%s%i too large\n", DRIVER_NAME, id);
+		return -EINVAL;
+	}
+	if (probed_devices[id]) {
+		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
+			DRIVER_NAME, id);
+		return -EBUSY;
+	}
+
+	probed_devices[id] = 1;
+
+	devt = MKDEV(xhwicap_major, xhwicap_minor + id);
+
+	drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL);
+	if (!drvdata) {
+		dev_err(dev, "Couldn't allocate device private record\n");
+		return -ENOMEM;
+	}
+	memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata));
+	dev_set_drvdata(dev, (void *)drvdata);
+
+	if (!regs_res) {
+		dev_err(dev, "Couldn't get registers resource\n");
+		retval = -EFAULT;
+		goto failed1;
+	}
+
+	drvdata->mem_start = regs_res->start;
+	drvdata->mem_end = regs_res->end;
+	drvdata->mem_size = regs_res->end - regs_res->start + 1;
+
+	if (!request_mem_region(drvdata->mem_start,
+					drvdata->mem_size, DRIVER_NAME)) {
+		dev_err(dev, "Couldn't lock memory region at %p\n",
+			(void *)regs_res->start);
+		retval = -EBUSY;
+		goto failed1;
+	}
+
+	drvdata->devt = devt;
+	drvdata->dev = dev;
+	drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size);
+	if (!drvdata->base_address) {
+		dev_err(dev, "ioremap() failed\n");
+		goto failed2;
+	}
+
+	drvdata->config = config;
+	drvdata->config_regs = config_regs;
+
+	init_MUTEX(&drvdata->sem);
+	drvdata->is_open = 0;
+
+	dev_info(dev, "ioremap %lx to %p with size %x\n",
+		 (unsigned long int)drvdata->mem_start,
+			drvdata->base_address, drvdata->mem_size);
+
+	cdev_init(&drvdata->cdev, &hwicap_fops);
+	drvdata->cdev.owner = THIS_MODULE;
+	retval = cdev_add(&drvdata->cdev, devt, 1);
+	if (retval) {
+		dev_err(dev, "cdev_add() failed\n");
+		goto failed3;
+	}
+	/*  devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */
+	class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME);
+	return 0;		/* success */
+
+ failed3:
+	iounmap(drvdata->base_address);
+
+ failed2:
+	release_mem_region(regs_res->start, drvdata->mem_size);
+
+ failed1:
+	kfree(drvdata);
+
+	return retval;
+}
+
+static struct hwicap_driver_config buffer_icap_config = {
+	.get_configuration = buffer_icap_get_configuration,
+	.set_configuration = buffer_icap_set_configuration,
+	.reset = buffer_icap_reset,
+};
+
+static struct hwicap_driver_config fifo_icap_config = {
+	.get_configuration = fifo_icap_get_configuration,
+	.set_configuration = fifo_icap_set_configuration,
+	.reset = fifo_icap_reset,
+};
+
+static int __devexit hwicap_remove(struct device *dev)
+{
+	struct hwicap_drvdata *drvdata;
+
+	drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
+
+	if (!drvdata)
+            return 0;
+        
+        class_device_destroy(icap_class, drvdata->devt);
+        cdev_del(&drvdata->cdev);
+        iounmap(drvdata->base_address);
+        release_mem_region(drvdata->mem_start, drvdata->mem_size);
+        kfree(drvdata);
+        dev_set_drvdata(dev, NULL);
+        probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0;
+	
+	return 0;		/* success */
+}
+
+static int __devinit hwicap_drv_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	const struct config_registers *regs;
+	const char *family;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	/* It's most likely that we're using V4, if the family is not
+	   specified */
+	regs = &v4_config_registers;
+	family = pdev->dev.platform_data;
+
+	if (family) {
+		if (!strcmp(family, "virtex2p")) {
+			regs = &v2_config_registers;
+		} else if (!strcmp(family, "virtex4")) {
+			regs = &v4_config_registers;
+		} else if (!strcmp(family, "virtex5")) {
+			regs = &v5_config_registers;
+		}
+	}
+
+	return hwicap_setup(&pdev->dev, pdev->id, res,
+			&buffer_icap_config, regs);
+}
+
+static int __devexit hwicap_drv_remove(struct platform_device *pdev)
+{
+	return hwicap_remove(&pdev->dev);
+}
+
+static struct platform_driver hwicap_platform_driver = {
+	.probe = hwicap_drv_probe,
+	.remove = hwicap_drv_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * OF bus binding
+ */
+
+#if defined(CONFIG_OF)
+static int __devinit
+hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
+{
+	struct resource res;
+	const unsigned int *id;
+	const char *family;
+	int rc;
+	const struct hwicap_driver_config *config = match->data;
+	const struct config_registers *regs;
+
+	dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match);
+
+	rc = of_address_to_resource(op->node, 0, &res);
+	if (rc) {
+		dev_err(&op->dev, "invalid address\n");
+		return rc;
+	}
+
+	id = of_get_property(op->node, "port-number", NULL);
+
+	/* It's most likely that we're using V4, if the family is not
+	   specified */
+	regs = &v4_config_registers;
+	family = of_get_property(op->node, "xlnx,family", NULL);
+
+	if (family) {
+		if (!strcmp(family, "virtex2p")) {
+			regs = &v2_config_registers;
+		} else if (!strcmp(family, "virtex4")) {
+			regs = &v4_config_registers;
+		} else if (!strcmp(family, "virtex5")) {
+			regs = &v5_config_registers;
+		}
+	}
+	return hwicap_setup(&op->dev, id ? *id : -1, &res, config,
+			regs);
+}
+
+static int __devexit hwicap_of_remove(struct of_device *op)
+{
+	return hwicap_remove(&op->dev);
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id __devinit hwicap_of_match[] = {
+	{ .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config},
+	{ .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hwicap_of_match);
+
+static struct of_platform_driver hwicap_of_driver = {
+	.owner = THIS_MODULE,
+	.name = DRIVER_NAME,
+	.match_table = hwicap_of_match,
+	.probe = hwicap_of_probe,
+	.remove = __devexit_p(hwicap_of_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+};
+
+/* Registration helpers to keep the number of #ifdefs to a minimum */
+static inline int __devinit hwicap_of_register(void)
+{
+	pr_debug("hwicap: calling of_register_platform_driver()\n");
+	return of_register_platform_driver(&hwicap_of_driver);
+}
+
+static inline void __devexit hwicap_of_unregister(void)
+{
+	of_unregister_platform_driver(&hwicap_of_driver);
+}
+#else /* CONFIG_OF */
+/* CONFIG_OF not enabled; do nothing helpers */
+static inline int __devinit hwicap_of_register(void) { return 0; }
+static inline void __devexit hwicap_of_unregister(void) { }
+#endif /* CONFIG_OF */
+
+static int __devinit hwicap_module_init(void)
+{
+	dev_t devt;
+	int retval;
+
+	icap_class = class_create(THIS_MODULE, "xilinx_config");
+
+	if (xhwicap_major) {
+		devt = MKDEV(xhwicap_major, xhwicap_minor);
+		retval = register_chrdev_region(
+				devt,
+				HWICAP_DEVICES,
+				DRIVER_NAME);
+		if (retval < 0)
+			return retval;
+	} else {
+		retval = alloc_chrdev_region(&devt,
+				xhwicap_minor,
+				HWICAP_DEVICES,
+				DRIVER_NAME);
+		if (retval < 0)
+			return retval;
+		xhwicap_major = MAJOR(devt);
+	}
+
+	retval = platform_driver_register(&hwicap_platform_driver);
+
+	if (retval)
+		goto failed1;
+
+	retval = hwicap_of_register();
+
+	if (retval)
+		goto failed2;
+
+	return retval;
+
+ failed2:
+	platform_driver_unregister(&hwicap_platform_driver);
+
+ failed1:
+	unregister_chrdev_region(devt, HWICAP_DEVICES);
+
+	return retval;
+}
+
+static void __devexit hwicap_module_cleanup(void)
+{
+	dev_t devt = MKDEV(xhwicap_major, xhwicap_minor);
+
+	class_destroy(icap_class);
+
+	platform_driver_unregister(&hwicap_platform_driver);
+
+	hwicap_of_unregister();
+
+	unregister_chrdev_region(devt, HWICAP_DEVICES);
+}
+
+module_init(hwicap_module_init);
+module_exit(hwicap_module_cleanup);
+
+MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group");
+MODULE_DESCRIPTION("Xilinx ICAP Port Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
new file mode 100644
index 0000000..b6b47d0
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
@@ -0,0 +1,193 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2007 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_HWICAP_H_	/* prevent circular inclusions */
+#define XILINX_HWICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+struct hwicap_drvdata {
+	u32 write_buffer_in_use;  /* Always in [0,3] */
+	u8 write_buffer[4];
+	u32 read_buffer_in_use;	  /* Always in [0,3] */
+	u8 read_buffer[4];
+	u32 mem_start;		  /* phys. address of the control registers */
+	u32 mem_end;		  /* phys. address of the control registers */
+	u32 mem_size;
+	void __iomem *base_address;/* virt. address of the control registers */
+
+	struct device *dev;
+	struct cdev cdev;	/* Char device structure */
+	dev_t devt;
+
+	const struct hwicap_driver_config *config;
+	const struct config_registers *config_regs;
+	void *private_data;
+	bool is_open;
+	bool is_accessing;
+};
+
+struct hwicap_driver_config {
+	int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data,
+			u32 size);
+	int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data,
+			u32 size);
+	void (*reset)(struct hwicap_drvdata *drvdata);
+};
+
+/* Number of times to poll the done regsiter */
+#define XHI_MAX_RETRIES     10
+
+/************ Constant Definitions *************/
+
+#define XHI_PAD_FRAMES              0x1
+
+/* Mask for calculating configuration packet headers */
+#define XHI_WORD_COUNT_MASK_TYPE_1  0x7FFUL
+#define XHI_WORD_COUNT_MASK_TYPE_2  0x1FFFFFUL
+#define XHI_TYPE_MASK               0x7
+#define XHI_REGISTER_MASK           0xF
+#define XHI_OP_MASK                 0x3
+
+#define XHI_TYPE_SHIFT              29
+#define XHI_REGISTER_SHIFT          13
+#define XHI_OP_SHIFT                27
+
+#define XHI_TYPE_1                  1
+#define XHI_TYPE_2                  2
+#define XHI_OP_WRITE                2
+#define XHI_OP_READ                 1
+
+/* Address Block Types */
+#define XHI_FAR_CLB_BLOCK           0
+#define XHI_FAR_BRAM_BLOCK          1
+#define XHI_FAR_BRAM_INT_BLOCK      2
+
+struct config_registers {
+	u32 CRC;
+	u32 FAR;
+	u32 FDRI;
+	u32 FDRO;
+	u32 CMD;
+	u32 CTL;
+	u32 MASK;
+	u32 STAT;
+	u32 LOUT;
+	u32 COR;
+	u32 MFWR;
+	u32 FLR;
+	u32 KEY;
+	u32 CBC;
+	u32 IDCODE;
+	u32 AXSS;
+	u32 C0R_1;
+	u32 CSOB;
+	u32 WBSTAR;
+	u32 TIMER;
+	u32 BOOTSTS;
+	u32 CTL_1;
+};
+
+/* Configuration Commands */
+#define XHI_CMD_NULL                0
+#define XHI_CMD_WCFG                1
+#define XHI_CMD_MFW                 2
+#define XHI_CMD_DGHIGH              3
+#define XHI_CMD_RCFG                4
+#define XHI_CMD_START               5
+#define XHI_CMD_RCAP                6
+#define XHI_CMD_RCRC                7
+#define XHI_CMD_AGHIGH              8
+#define XHI_CMD_SWITCH              9
+#define XHI_CMD_GRESTORE            10
+#define XHI_CMD_SHUTDOWN            11
+#define XHI_CMD_GCAPTURE            12
+#define XHI_CMD_DESYNCH             13
+#define XHI_CMD_IPROG               15 /* Only in Virtex5 */
+#define XHI_CMD_CRCC                16 /* Only in Virtex5 */
+#define XHI_CMD_LTIMER              17 /* Only in Virtex5 */
+
+/* Packet constants */
+#define XHI_SYNC_PACKET             0xAA995566UL
+#define XHI_DUMMY_PACKET            0xFFFFFFFFUL
+#define XHI_NOOP_PACKET             (XHI_TYPE_1 << XHI_TYPE_SHIFT)
+#define XHI_TYPE_2_READ ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \
+			(XHI_OP_READ << XHI_OP_SHIFT))
+
+#define XHI_TYPE_2_WRITE ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \
+			(XHI_OP_WRITE << XHI_OP_SHIFT))
+
+#define XHI_TYPE2_CNT_MASK          0x07FFFFFF
+
+#define XHI_TYPE_1_PACKET_MAX_WORDS 2047UL
+#define XHI_TYPE_1_HEADER_BYTES     4
+#define XHI_TYPE_2_HEADER_BYTES     8
+
+/* Constant to use for CRC check when CRC has been disabled */
+#define XHI_DISABLED_AUTO_CRC       0x0000DEFCUL
+
+/**
+ * hwicap_type_1_read: Generates a Type 1 read packet header.
+ * @parameter: Register is the address of the register to be read back.
+ *
+ * Generates a Type 1 read packet header, which is used to indirectly
+ * read registers in the configuration logic.  This packet must then
+ * be sent through the icap device, and a return packet received with
+ * the information.
+ **/
+static inline u32 hwicap_type_1_read(u32 Register)
+{
+	return (XHI_TYPE_1 << XHI_TYPE_SHIFT) |
+		(Register << XHI_REGISTER_SHIFT) |
+		(XHI_OP_READ << XHI_OP_SHIFT);
+}
+
+/**
+ * hwicap_type_1_write: Generates a Type 1 write packet header
+ * @parameter: Register is the address of the register to be read back.
+ **/
+static inline u32 hwicap_type_1_write(u32 Register)
+{
+	return (XHI_TYPE_1 << XHI_TYPE_SHIFT) |
+		(Register << XHI_REGISTER_SHIFT) |
+		(XHI_OP_WRITE << XHI_OP_SHIFT);
+}
+
+#endif
-- 
1.5.3.4-dirty

^ permalink raw reply related

* Re: 8360 custom board, ucc_geth TX errors on longer(?) packets
From: Steven Hein @ 2008-02-01 20:05 UTC (permalink / raw)
  To: Kim Phillips; +Cc: linuxppc-embedded
In-Reply-To: <20080201135324.8372268e.kim.phillips@freescale.com>

Kim Phillips wrote:
> On Fri, 01 Feb 2008 12:52:25 -0600
> Steven Hein <ssh@sgi.com> wrote:
>
>   
>> The one main difference in this board is how eth0 is wired.
>> We have a Broadcom GbE switch part, and UCC1 eth is wired
>> directly to that switch (no PHY).   (This where I needed to
>>     
>
> sounds like you ran into some h/w errata.  if on rgmii, you might
> want to find a way to program the switch for rgmii with internal delay
> (8360 rev.2 rgmii-id rx & tx; 8360rev2.1 rgmii-rxid (i.e. for rx
> only)).  If not, I'd contact fsl tech support directly.
>
> Kim
>   

I would suspect HW.....but this WORKS with the 2.6.16 kernel
I was using!    That's why I suspect that I still don't have
something configured right in my device tree, or something
else I missed in the new kernel.    But I can't
figure out what it is.... :-(    I've poured over the code in
the old versus new (both the ucc_geth driver and the platform
initialization in the old, and the device tree in the new)
and can't figure out what I missed!   And like I said, a
kernel with the same config (other than changing the platform)
works on my MPC8360E-MDS board.   Granted, that doesn't have
this direct switch connection......

I did look at the code related to the HW errata (QE_ENET18).
But we're using GMII to the switch....and that workaround
code wasn't in active in my old kernel (it was there, but
commented out).

Any other thoughts?    Has anyone seen this symptom before?

Steve

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Steve Hein (ssh@sgi.com)              Engineering Diagnostics/Software
Silicon Graphics, Inc.                          
1168 Industrial Blvd.                 Phone: (715) 726-8410
Chippewa Falls, WI 54729              Fax:   (715) 726-6715
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

^ permalink raw reply

* Re: [RFC/PATCH] powerpc: Add irqtrace support to 32-bit powerpc
From: Johannes Berg @ 2008-02-01 20:09 UTC (permalink / raw)
  To: Dale Farnsworth; +Cc: linuxppc-dev
In-Reply-To: <20080201195040.GA8950@farnsworth.org>

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


On Fri, 2008-02-01 at 12:50 -0700, Dale Farnsworth wrote:
> This adds the low level irq tracing hooks for 32-bit powerpc.
> This patch applies on top of Benjamin Herrenschmidt's patch that
> provides similar support for 64-bit powerpc.  See
> <http://ozlabs.org/pipermail/linuxppc-dev/2007-October/044361.html>

Cool. Except that Ben's patch doesn't work for me on my quad G5 and I
still haven't had time to figure out why... We should try to get this up
for .26 now that we obviously missed .25. Anybody else have a quad G5 to
test on?

I can test this patch on my powerbook too.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply

* Re: [PATCH] [POWERPC] Xilinx: hwicap driver
From: Grant Likely @ 2008-02-01 20:11 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: linuxppc-dev, ntl
In-Reply-To: <20080201200324.66A91FF004D@mail157-sin.bigfish.com>

On 2/1/08, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
> This includes code for new fifo-based xps_hwicap in addition to the
> older opb_hwicap, which has a significantly different interface.  The
> common code between the two drivers is largely shared.
>
> Significant differences exists between this driver and what is
> supported in the EDK drivers.  In particular, most of the
> architecture-specific code for reconfiguring individual FPGA resources
> has been removed.  This functionality is likely better provided in a
> user-space support library.  In addition, read and write access is
> supported.  In addition, although the xps_hwicap cores support
> interrupt-driver mode, this driver only supports polled operation, in
> order to make the code simpler, and since the interrupt processing
> overhead is likely to slow down the throughput under Linux.
>
> Signed-off-by: Stephen Neuendorffer <stephen.neuendorffer@xilinx.com>
>
> Fixed to add mutexes, and a few style issues.

Inconsistent indentation (tabs vs. spaces) in some places.  Otherwise:

Acked-by: Grant Likely <grant.likely@secretlab.ca>

g.

> +static int __devexit hwicap_remove(struct device *dev)
> +{
> +       struct hwicap_drvdata *drvdata;
> +
> +       drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
> +
> +       if (!drvdata)
> +            return 0;
> +
> +        class_device_destroy(icap_class, drvdata->devt);
> +        cdev_del(&drvdata->cdev);
> +        iounmap(drvdata->base_address);
> +        release_mem_region(drvdata->mem_start, drvdata->mem_size);
> +        kfree(drvdata);
> +        dev_set_drvdata(dev, NULL);
> +        probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0;
> +
> +       return 0;               /* success */
> +}

Here in particular


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

^ permalink raw reply

* [PATCH] Remove old definition of setup_per_cpu_areas
From: Bastian Blank @ 2008-02-01 20:16 UTC (permalink / raw)
  To: linuxppc-dev, akpm; +Cc: linux-kernel

Remove old definition of setup_per_cpu_areas.

The definition is done depending on the new config variable
HAVE_SETUP_PER_CPU_AREA. 

Signed-off-by: Bastian Blank <waldi@debian.org>

diff --git a/include/asm-powerpc/percpu.h b/include/asm-powerpc/percpu.h
index cc1cbf6..f309d34 100644
--- a/include/asm-powerpc/percpu.h
+++ b/include/asm-powerpc/percpu.h
@@ -30,8 +30,6 @@ do {								\
 		       (src), (size));				\
 } while (0)
 
-extern void setup_per_cpu_areas(void);
-
 #else /* ! SMP */
 
 #define per_cpu(var, cpu)			(*((void)(cpu), &per_cpu__##var))

^ permalink raw reply related

* [PATCH] Fix ext4 bitops
From: Bastian Blank @ 2008-02-01 20:02 UTC (permalink / raw)
  To: linuxppc-dev, akpm; +Cc: linux-kernel

Fix ext4 bitops.

Signed-off-by: Bastian Blank <waldi@debian.org>

diff --git a/include/asm-powerpc/bitops.h b/include/asm-powerpc/bitops.h
index 220d9a7..d0980df 100644
--- a/include/asm-powerpc/bitops.h
+++ b/include/asm-powerpc/bitops.h
@@ -363,6 +363,8 @@ unsigned long generic_find_next_le_bit(const unsigned long *addr,
 				    unsigned long size, unsigned long offset);
 /* Bitmap functions for the ext2 filesystem */
 
+#include <asm-generic/bitops/le.h>
+
 #define ext2_set_bit(nr,addr) \
 	__test_and_set_le_bit((nr), (unsigned long*)addr)
 #define ext2_clear_bit(nr, addr) \

^ permalink raw reply related

* Re: [PATCH 2/7] [POWERPC] Xilinx: clear data caches.
From: Grant Likely @ 2008-02-01 20:40 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: linuxppc-dev, simekm2
In-Reply-To: <20071214003648.68E5EC68059@mail177-blu.bigfish.com>

On 12/13/07, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
>
> Unfortunately, I think your right... The asm code seems so clean at the
> moment, I don't really want be the one to start tarnishing it with a
> bunch of little fixups.. :)

No problem, we can use a little shim that executes this stuff before
going into crt0.S.  I'm working on this right now.

g.

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

^ permalink raw reply

* Re: [PATCH v2] 82xx: MGCOGE support
From: Scott Wood @ 2008-02-01 20:45 UTC (permalink / raw)
  To: hs; +Cc: linuxppc-dev
In-Reply-To: <47A04813.7070306@denx.de>

Heiko Schocher wrote:
> Hello,
> 
> The following patch adds support for the mpc8247 based
> board MGCOGE from keymile.
> 
> changes to the previous patchset:
> 
> - Added the suggestions from Scott Wood:
>   http://ozlabs.org/pipermail/linuxppc-dev/2008-January/050957.html
> 
> - The patch http://ozlabs.org/pipermail/linuxppc-dev/2008-January/050931.html
>   is no longer needed.
> 
> Signed-off-by: Heiko Schocher <hs@denx.de>

Acked-by: Scott Wood <scottwood@freescale.com>

-Scott

^ permalink raw reply

* Re: PATCH[1/1] 8xx: Add clock-frequency to Adder875 and mpc885ads board ports
From: Scott Wood @ 2008-02-01 21:00 UTC (permalink / raw)
  To: Bryan O'Donoghue; +Cc: linuxppc-dev
In-Reply-To: <1201736088.3466.3.camel@neuromancer.mindspace>

Bryan O'Donoghue wrote:
> Redo the addition of the clock-frequency parameter to the Adder875 .dts
> so that the values are decimal rather then hex.
> 
> 
> Signed-off-by: Bryan O'Donoghue <bodonoghue@codehermit.ie>
> ---
> 
> diff --git a/arch/powerpc/boot/dts/adder875-redboot.dts
> b/arch/powerpc/boot/dts/adder875-redboot.dts
> index 7c25d96..c508f3c 100644
> --- a/arch/powerpc/boot/dts/adder875-redboot.dts
> +++ b/arch/powerpc/boot/dts/adder875-redboot.dts
> @@ -149,6 +149,7 @@
>                                 compatible = "fsl,mpc875-brg",
>                                              "fsl,cpm1-brg",
>                                              "fsl,cpm-brg";
> +                               clock-frequency = <50000000>;
>                                 reg = <0x9f0 0x10>;
>                         };

This patch is whitespace-mangled (all tabs are spaces, even in quoted 
sections).

-Scott

^ permalink raw reply

* RE: 83xx immap_qe.h -> SIR type def error?
From: Russell McGuire @ 2008-02-01 21:03 UTC (permalink / raw)
  To: 'Kumar Gala'; +Cc: linuxppc-embedded
In-Reply-To: <1ADE91E0-282D-4455-9754-886846B3EB90@kernel.crashing.org>

Kumar,

Yes in the main memeory map they are just listed as 1K RAM blocks.
However, in the UM Section 36.6.1 <pg 36-12 or pg 1728 in the PDF>. 

It gives the breakout for the RAM, which clearly indicates 16 bit fields.
<Here is a short clip from Figure 36-8>
 
Access: Read/Write
0 1 2 3 4 5 6 7 10 11 13 14 15
MCC SWTR SSEL 1 SSEL 2 SSEL 3 SSEL 4 SGS CSEL CNT BYT LST 
Figure 36-8. SI RAM Entry for UCC

Honest, mistake as if I were writing the header file I'd not have time to
ready all 2000+ pages of the UM. We find these only as somebody goes in an
tries to use them. 
And I am guessing not a lot of customers use the SI block.

-Russ
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Friday, February 01, 2008 6:56 AM
> To: rmcguire@videopresence.com
> Cc: linuxppc-embedded@ozlabs.org
> Subject: Re: 83xx immap_qe.h -> SIR type def error?
> 
> 
> On Feb 1, 2008, at 5:47 AM, Russell McGuire wrote:
> 
> > All Freescale,
> >
> > Not sure if this is the place to post this, but I have run across
> > what I
> > consider to be a possible type error in the immap_qe.h file, for the
> > asm/powerpc branch.
> >
> > In the file immap_qe.h
> >
> > /* SI Routing Tables */
> > struct sir {
> > 	u8  tx[0x400];
> > 	u8  rx[0x400];
> > 	u8  res0[0x800];
> > }
> >
> > Shouldn't these types be defined as __be16 ?
> >
> > According to the Freescale manual this is a 16 bit field, not an 8-bit
> > field.
> >
> > Spent an hour trying to figure out why I couldn't fill this field
> > out with
> > upper 8 bits last night.
> >
> > Thoughts?
> 
> I'm guessing it was done this way since they are just looked as base
> offsets.  Where in the UM do you see anything about them being 16-bit
> quantities?  (I'm really know little about this).
> 
> - k

^ permalink raw reply

* Re: [PATCH] Remove old definition of setup_per_cpu_areas
From: Bastian Blank @ 2008-02-01 21:11 UTC (permalink / raw)
  To: linuxppc-dev, akpm, linux-kernel
In-Reply-To: <20080201201615.GA29187@wavehammer.waldi.eu.org>

On Fri, Feb 01, 2008 at 09:16:15PM +0100, Bastian Blank wrote:
> Remove old definition of setup_per_cpu_areas.

This was completely wrong. Lets try it again.

Adopt x86 percpu changes for powerpc and ia64.

dd5af90a7f3d79e04b7eace9a98644dbf2038f4d replaced the config variable
ARCH_SETS_UP_PER_CPU_AREA with HAVE_SETUP_PER_CPU_AREA and moved
percpu_modcopy into kernel/module.c.

Do the same for ia64 and powerpc.

| init/main.c:376: error: static declaration of ‘setup_per_cpu_areas’ follows non-static declaration
| include/asm/percpu.h:33: error: previous declaration of ‘setup_per_cpu_areas’ was here

Signed-off-by: Bastian Blank <waldi@debian.org>

diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 5a41e75..c9307c9 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -80,7 +80,7 @@ config GENERIC_TIME_VSYSCALL
 	bool
 	default y
 
-config ARCH_SETS_UP_PER_CPU_AREA
+config HAVE_SETUP_PER_CPU_AREA
 	def_bool y
 
 config DMI
diff --git a/arch/ia64/kernel/module.c b/arch/ia64/kernel/module.c
index e699eb6..bbcdcfd 100644
--- a/arch/ia64/kernel/module.c
+++ b/arch/ia64/kernel/module.c
@@ -941,13 +941,3 @@ module_arch_cleanup (struct module *mod)
 		unw_remove_unwind_table(mod->arch.core_unw_table);
 }
 
-#ifdef CONFIG_SMP
-void
-percpu_modcopy (void *pcpudst, const void *src, unsigned long size)
-{
-	unsigned int i;
-	for_each_possible_cpu(i) {
-		memcpy(pcpudst + per_cpu_offset(i), src, size);
-	}
-}
-#endif /* CONFIG_SMP */
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index fb85f6b..bb16443 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -42,7 +42,7 @@ config GENERIC_HARDIRQS
 	bool
 	default y
 
-config ARCH_SETS_UP_PER_CPU_AREA
+config HAVE_SETUP_PER_CPU_AREA
 	def_bool PPC64
 
 config IRQ_PER_CPU
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index 26f5791..73fc05d 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -66,7 +66,7 @@ config AUDIT_ARCH
 	bool
 	default y
 
-config ARCH_SETS_UP_PER_CPU_AREA
+config HAVE_SETUP_PER_CPU_AREA
 	def_bool y
 
 config ARCH_NO_VIRT_TO_BUS
diff --git a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h
index 0095bcf..a4af49f 100644
--- a/include/asm-ia64/percpu.h
+++ b/include/asm-ia64/percpu.h
@@ -38,8 +38,9 @@ DECLARE_PER_CPU(unsigned long, local_per_cpu_offset);
 #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __ia64_per_cpu_var(local_per_cpu_offset)))
 #define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __ia64_per_cpu_var(local_per_cpu_offset)))
 
-extern void percpu_modcopy(void *pcpudst, const void *src, unsigned long size);
+#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
 extern void setup_per_cpu_areas (void);
+#endif
 extern void *per_cpu_init(void);
 
 #else /* ! SMP */
diff --git a/include/asm-powerpc/percpu.h b/include/asm-powerpc/percpu.h
index cc1cbf6..ea8cbb7 100644
--- a/include/asm-powerpc/percpu.h
+++ b/include/asm-powerpc/percpu.h
@@ -21,16 +21,9 @@
 #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __my_cpu_offset()))
 #define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, local_paca->data_offset))
 
-/* A macro to avoid #include hell... */
-#define percpu_modcopy(pcpudst, src, size)			\
-do {								\
-	unsigned int __i;					\
-	for_each_possible_cpu(__i)				\
-		memcpy((pcpudst)+__per_cpu_offset(__i),		\
-		       (src), (size));				\
-} while (0)
-
+#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
 extern void setup_per_cpu_areas(void);
+#endif
 
 #else /* ! SMP */
 

^ permalink raw reply related

* [PATCH] [POWERPC] Xilinx: hwicap driver
From: Stephen Neuendorffer @ 2008-02-01 21:12 UTC (permalink / raw)
  To: linuxppc-dev, grant.likely, jacmet, ntl
In-Reply-To: <fa686aa40802011211n1b18d4ddg3fdb95f274221dd6@mail.gmail.com>

This includes code for new fifo-based xps_hwicap in addition to the
older opb_hwicap, which has a significantly different interface.  The
common code between the two drivers is largely shared.

Significant differences exists between this driver and what is
supported in the EDK drivers.  In particular, most of the
architecture-specific code for reconfiguring individual FPGA resources
has been removed.  This functionality is likely better provided in a
user-space support library.  In addition, read and write access is
supported.  In addition, although the xps_hwicap cores support
interrupt-driver mode, this driver only supports polled operation, in
order to make the code simpler, and since the interrupt processing
overhead is likely to slow down the throughput under Linux.

Signed-off-by: Stephen Neuendorffer <stephen.neuendorffer@xilinx.com>

Fixed to add mutexes, and a few style issues.

Acked-by: Grant Likely <grant.likely@secretlab.ca>

---

I will get this right eventually. :)
---
 drivers/char/Kconfig                       |    7 +
 drivers/char/Makefile                      |    1 +
 drivers/char/xilinx_hwicap/Makefile        |    7 +
 drivers/char/xilinx_hwicap/buffer_icap.c   |  380 ++++++++++++
 drivers/char/xilinx_hwicap/buffer_icap.h   |   57 ++
 drivers/char/xilinx_hwicap/fifo_icap.c     |  381 ++++++++++++
 drivers/char/xilinx_hwicap/fifo_icap.h     |   62 ++
 drivers/char/xilinx_hwicap/xilinx_hwicap.c |  904 ++++++++++++++++++++++++++++
 drivers/char/xilinx_hwicap/xilinx_hwicap.h |  193 ++++++
 9 files changed, 1992 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/xilinx_hwicap/Makefile
 create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.c
 create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.h
 create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.c
 create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.h
 create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.c
 create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ef1ed5d..157ae2a 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -831,6 +831,13 @@ config DTLK
 	  To compile this driver as a module, choose M here: the
 	  module will be called dtlk.
 
+config XILINX_HWICAP
+	tristate "Xilinx HWICAP Support"
+	depends on XILINX_VIRTEX
+	help
+	  This option enables support for Xilinx Internal Configuration
+	  Access Port (ICAP) driver.
+
 config R3964
 	tristate "Siemens R3964 line discipline"
 	---help---
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 07304d5..3a278a0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_EFI_RTC)		+= efirtc.o
 obj-$(CONFIG_SGI_DS1286)	+= ds1286.o
 obj-$(CONFIG_SGI_IP27_RTC)	+= ip27-rtc.o
 obj-$(CONFIG_DS1302)		+= ds1302.o
+obj-$(CONFIG_XILINX_HWICAP)    += xilinx_hwicap/
 ifeq ($(CONFIG_GENERIC_NVRAM),y)
   obj-$(CONFIG_NVRAM)	+= generic_nvram.o
 else
diff --git a/drivers/char/xilinx_hwicap/Makefile b/drivers/char/xilinx_hwicap/Makefile
new file mode 100644
index 0000000..5491cbc
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Xilinx OPB hwicap driver
+#
+
+obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap_m.o 
+ 
+xilinx_hwicap_m-y := xilinx_hwicap.o fifo_icap.o buffer_icap.o
diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c
new file mode 100644
index 0000000..dfea2bd
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/buffer_icap.c
@@ -0,0 +1,380 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 "buffer_icap.h"
+
+/* Indicates how many bytes will fit in a buffer. (1 BRAM) */
+#define XHI_MAX_BUFFER_BYTES        2048
+#define XHI_MAX_BUFFER_INTS         (XHI_MAX_BUFFER_BYTES >> 2)
+
+/* File access and error constants */
+#define XHI_DEVICE_READ_ERROR       -1
+#define XHI_DEVICE_WRITE_ERROR      -2
+#define XHI_BUFFER_OVERFLOW_ERROR   -3
+
+#define XHI_DEVICE_READ             0x1
+#define XHI_DEVICE_WRITE            0x0
+
+/* Constants for checking transfer status */
+#define XHI_CYCLE_DONE              0
+#define XHI_CYCLE_EXECUTING         1
+
+/* buffer_icap register offsets */
+
+/* Size of transfer, read & write */
+#define XHI_SIZE_REG_OFFSET        0x800L
+/* offset into bram, read & write */
+#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L
+/* Read not Configure, direction of transfer.  Write only */
+#define XHI_RNC_REG_OFFSET         0x808L
+/* Indicates transfer complete. Read only */
+#define XHI_STATUS_REG_OFFSET      0x80CL
+
+/* Constants for setting the RNC register */
+#define XHI_CONFIGURE              0x0UL
+#define XHI_READBACK               0x1UL
+
+/* Constants for the Done register */
+#define XHI_NOT_FINISHED           0x0UL
+#define XHI_FINISHED               0x1UL
+
+#define XHI_BUFFER_START 0
+
+/**
+ * buffer_icap_get_status: Get the contents of the status register.
+ * @parameter base_address: is the base address of the device
+ *
+ * The status register contains the ICAP status and the done bit.
+ *
+ * D8 - cfgerr
+ * D7 - dalign
+ * D6 - rip
+ * D5 - in_abort_l
+ * D4 - Always 1
+ * D3 - Always 1
+ * D2 - Always 1
+ * D1 - Always 1
+ * D0 - Done bit
+ **/
+static inline u32 buffer_icap_get_status(void __iomem *base_address)
+{
+	return in_be32(base_address + XHI_STATUS_REG_OFFSET);
+}
+
+/**
+ * buffer_icap_get_bram: Reads data from the storage buffer bram.
+ * @parameter base_address: contains the base address of the component.
+ * @parameter offset: The word offset from which the data should be read.
+ *
+ * A bram is used as a configuration memory cache.  One frame of data can
+ * be stored in this "storage buffer".
+ **/
+static inline u32 buffer_icap_get_bram(void __iomem *base_address,
+		u32 offset)
+{
+	return in_be32(base_address + (offset << 2));
+}
+
+/**
+ * buffer_icap_busy: Return true if the icap device is busy
+ * @parameter base_address: is the base address of the device
+ *
+ * The queries the low order bit of the status register, which
+ * indicates whether the current configuration or readback operation
+ * has completed.
+ **/
+static inline bool buffer_icap_busy(void __iomem *base_address)
+{
+	return (buffer_icap_get_status(base_address) & 1) == XHI_NOT_FINISHED;
+}
+
+/**
+ * buffer_icap_busy: Return true if the icap device is not busy
+ * @parameter base_address: is the base address of the device
+ *
+ * The queries the low order bit of the status register, which
+ * indicates whether the current configuration or readback operation
+ * has completed.
+ **/
+static inline bool buffer_icap_done(void __iomem *base_address)
+{
+	return (buffer_icap_get_status(base_address) & 1) == XHI_FINISHED;
+}
+
+/**
+ * buffer_icap_set_size: Set the size register.
+ * @parameter base_address: is the base address of the device
+ * @parameter data: The size in bytes.
+ *
+ * The size register holds the number of 8 bit bytes to transfer between
+ * bram and the icap (or icap to bram).
+ **/
+static inline void buffer_icap_set_size(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_SIZE_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_mSetoffsetReg: Set the bram offset register.
+ * @parameter base_address: contains the base address of the device.
+ * @parameter data: is the value to be written to the data register.
+ *
+ * The bram offset register holds the starting bram address to transfer
+ * data from during configuration or write data to during readback.
+ **/
+static inline void buffer_icap_set_offset(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_set_rnc: Set the RNC (Readback not Configure) register.
+ * @parameter base_address: contains the base address of the device.
+ * @parameter data: is the value to be written to the data register.
+ *
+ * The RNC register determines the direction of the data transfer.  It
+ * controls whether a configuration or readback take place.  Writing to
+ * this register initiates the transfer.  A value of 1 initiates a
+ * readback while writing a value of 0 initiates a configuration.
+ **/
+static inline void buffer_icap_set_rnc(void __iomem *base_address,
+		u32 data)
+{
+	out_be32(base_address + XHI_RNC_REG_OFFSET, data);
+}
+
+/**
+ * buffer_icap_set_bram: Write data to the storage buffer bram.
+ * @parameter base_address: contains the base address of the component.
+ * @parameter offset: The word offset at which the data should be written.
+ * @parameter data: The value to be written to the bram offset.
+ *
+ * A bram is used as a configuration memory cache.  One frame of data can
+ * be stored in this "storage buffer".
+ **/
+static inline void buffer_icap_set_bram(void __iomem *base_address,
+		u32 offset, u32 data)
+{
+	out_be32(base_address + (offset << 2), data);
+}
+
+/**
+ * buffer_icap_device_read: Transfer bytes from ICAP to the storage buffer.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter offset: The storage buffer start address.
+ * @parameter count: The number of words (32 bit) to read from the
+ *           device (ICAP).
+ **/
+static int buffer_icap_device_read(struct hwicap_drvdata *drvdata,
+		u32 offset, u32 count)
+{
+
+	s32 retries = 0;
+	void __iomem *base_address = drvdata->base_address;
+
+	if (buffer_icap_busy(base_address))
+		return -EBUSY;
+
+	if ((offset + count) > XHI_MAX_BUFFER_INTS)
+		return -EINVAL;
+
+	/* setSize count*4 to get bytes. */
+	buffer_icap_set_size(base_address, (count << 2));
+	buffer_icap_set_offset(base_address, offset);
+	buffer_icap_set_rnc(base_address, XHI_READBACK);
+
+	while (buffer_icap_busy(base_address)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			return -EBUSY;
+	}
+	return 0;
+
+};
+
+/**
+ * buffer_icap_device_write: Transfer bytes from ICAP to the storage buffer.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter offset: The storage buffer start address.
+ * @parameter count: The number of words (32 bit) to read from the
+ *           device (ICAP).
+ **/
+static int buffer_icap_device_write(struct hwicap_drvdata *drvdata,
+		u32 offset, u32 count)
+{
+
+	s32 retries = 0;
+	void __iomem *base_address = drvdata->base_address;
+
+	if (buffer_icap_busy(base_address))
+		return -EBUSY;
+
+	if ((offset + count) > XHI_MAX_BUFFER_INTS)
+		return -EINVAL;
+
+	/* setSize count*4 to get bytes. */
+	buffer_icap_set_size(base_address, count << 2);
+	buffer_icap_set_offset(base_address, offset);
+	buffer_icap_set_rnc(base_address, XHI_CONFIGURE);
+
+	while (buffer_icap_busy(base_address)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			return -EBUSY;
+	}
+	return 0;
+
+};
+
+/**
+ * buffer_icap_reset: Reset the logic of the icap device.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Writing to the status register resets the ICAP logic in an internal
+ * version of the core.  For the version of the core published in EDK,
+ * this is a noop.
+ **/
+void buffer_icap_reset(struct hwicap_drvdata *drvdata)
+{
+    out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE);
+}
+
+/**
+ * buffer_icap_set_configuration: Load a partial bitstream from system memory.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Kernel address of the partial bitstream.
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ **/
+int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 size)
+{
+	int status;
+	s32 buffer_count = 0;
+	s32 num_writes = 0;
+	bool dirty = 0;
+	u32 i;
+	void __iomem *base_address = drvdata->base_address;
+
+	/* Loop through all the data */
+	for (i = 0, buffer_count = 0; i < size; i++) {
+
+		/* Copy data to bram */
+		buffer_icap_set_bram(base_address, buffer_count, data[i]);
+		dirty = 1;
+
+		if (buffer_count < XHI_MAX_BUFFER_INTS - 1) {
+			buffer_count++;
+			continue;
+		}
+
+		/* Write data to ICAP */
+		status = buffer_icap_device_write(
+				drvdata,
+				XHI_BUFFER_START,
+				XHI_MAX_BUFFER_INTS);
+		if (status != 0) {
+			/* abort. */
+			buffer_icap_reset(drvdata);
+			return status;
+		}
+
+		buffer_count = 0;
+		num_writes++;
+		dirty = 0;
+	}
+
+	/* Write unwritten data to ICAP */
+	if (dirty) {
+		/* Write data to ICAP */
+		status = buffer_icap_device_write(drvdata, XHI_BUFFER_START,
+					     buffer_count);
+		if (status != 0) {
+			/* abort. */
+			buffer_icap_reset(drvdata);
+		}
+		return status;
+	}
+
+	return 0;
+};
+
+/**
+ * buffer_icap_get_configuration: Read configuration data from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Address of the data representing the partial bitstream
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ **/
+int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 size)
+{
+	int status;
+	s32 buffer_count = 0;
+	s32 read_count = 0;
+	u32 i;
+	void __iomem *base_address = drvdata->base_address;
+
+	/* Loop through all the data */
+	for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) {
+		if (buffer_count == XHI_MAX_BUFFER_INTS) {
+			u32 words_remaining = size - i;
+			u32 words_to_read =
+				words_remaining <
+				XHI_MAX_BUFFER_INTS ? words_remaining :
+				XHI_MAX_BUFFER_INTS;
+
+			/* Read data from ICAP */
+			status = buffer_icap_device_read(
+					drvdata,
+					XHI_BUFFER_START,
+					words_to_read);
+			if (status != 0) {
+				/* abort. */
+				buffer_icap_reset(drvdata);
+				return status;
+			}
+
+			buffer_count = 0;
+			read_count++;
+		}
+
+		/* Copy data from bram */
+		data[i] = buffer_icap_get_bram(base_address, buffer_count);
+		buffer_count++;
+	}
+
+	return 0;
+};
diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h
new file mode 100644
index 0000000..0318495
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/buffer_icap.h
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_BUFFER_ICAP_H_	/* prevent circular inclusions */
+#define XILINX_BUFFER_ICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include "xilinx_hwicap.h"
+
+void buffer_icap_reset(struct hwicap_drvdata *drvdata);
+
+/* Loads a partial bitstream from system memory. */
+int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 Size);
+
+/* Loads a partial bitstream from system memory. */
+int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
+			     u32 Size);
+
+#endif
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c
new file mode 100644
index 0000000..0988314
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/fifo_icap.c
@@ -0,0 +1,381 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 "fifo_icap.h"
+
+/* Register offsets for the XHwIcap device. */
+#define XHI_GIER_OFFSET	0x1C  /* Device Global Interrupt Enable Reg */
+#define XHI_IPISR_OFFSET 0x20  /* Interrupt Status Register */
+#define XHI_IPIER_OFFSET 0x28  /* Interrupt Enable Register */
+#define XHI_WF_OFFSET 0x100 /* Write FIFO */
+#define XHI_RF_OFFSET 0x104 /* Read FIFO */
+#define XHI_SZ_OFFSET 0x108 /* Size Register */
+#define XHI_CR_OFFSET 0x10C /* Control Register */
+#define XHI_SR_OFFSET 0x110 /* Status Register */
+#define XHI_WFV_OFFSET 0x114 /* Write FIFO Vacancy Register */
+#define XHI_RFO_OFFSET 0x118 /* Read FIFO Occupancy Register */
+
+/* Device Global Interrupt Enable Register (GIER) bit definitions */
+
+#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */
+
+/**
+ * HwIcap Device Interrupt Status/Enable Registers
+ *
+ * Interrupt Status Register (IPISR) : This register holds the
+ * interrupt status flags for the device. These bits are toggle on
+ * write.
+ *
+ * Interrupt Enable Register (IPIER) : This register is used to enable
+ * interrupt sources for the device.
+ * Writing a '1' to a bit enables the corresponding interrupt.
+ * Writing a '0' to a bit disables the corresponding interrupt.
+ *
+ * IPISR/IPIER registers have the same bit definitions and are only defined
+ * once.
+ */
+#define XHI_IPIXR_RFULL_MASK 0x00000008 /* Read FIFO Full */
+#define XHI_IPIXR_WEMPTY_MASK 0x00000004 /* Write FIFO Empty */
+#define XHI_IPIXR_RDP_MASK 0x00000002 /* Read FIFO half full */
+#define XHI_IPIXR_WRP_MASK 0x00000001 /* Write FIFO half full */
+#define XHI_IPIXR_ALL_MASK 0x0000000F /* Mask of all interrupts */
+
+/* Control Register (CR) */
+#define XHI_CR_SW_RESET_MASK 0x00000008 /* SW Reset Mask */
+#define XHI_CR_FIFO_CLR_MASK 0x00000004 /* FIFO Clear Mask */
+#define XHI_CR_READ_MASK 0x00000002 /* Read from ICAP to FIFO */
+#define XHI_CR_WRITE_MASK 0x00000001 /* Write from FIFO to ICAP */
+
+/* Status Register (SR) */
+#define XHI_SR_CFGERR_N_MASK 0x00000100 /* Config Error Mask */
+#define XHI_SR_DALIGN_MASK 0x00000080 /* Data Alignment Mask */
+#define XHI_SR_RIP_MASK 0x00000040 /* Read back Mask */
+#define XHI_SR_IN_ABORT_N_MASK 0x00000020 /* Select Map Abort Mask */
+#define XHI_SR_DONE_MASK 0x00000001 /* Done bit Mask  */
+
+
+#define XHI_WFO_MAX_VACANCY 1024 /* Max Write FIFO Vacancy, in words */
+#define XHI_RFO_MAX_OCCUPANCY 256 /* Max Read FIFO Occupancy, in words */
+/* The maximum amount we can request from fifo_icap_get_configuration
+   at once, in bytes. */
+#define XHI_MAX_READ_TRANSACTION_WORDS 0xFFF
+
+
+/**
+ * fifo_icap_fifo_write: Write data to the write FIFO.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: the 32-bit value to be written to the FIFO.
+ *
+ * This function will silently fail if the fifo is full.
+ **/
+static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata,
+		u32 data)
+{
+	dev_dbg(drvdata->dev, "fifo_write: %x\n", data);
+	out_be32(drvdata->base_address + XHI_WF_OFFSET, data);
+}
+
+/**
+ * fifo_icap_fifo_read: Read data from the Read FIFO.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This function will silently fail if the fifo is empty.
+ **/
+static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata)
+{
+	u32 data = in_be32(drvdata->base_address + XHI_RF_OFFSET);
+	dev_dbg(drvdata->dev, "fifo_read: %x\n", data);
+	return data;
+}
+
+/**
+ * fifo_icap_set_read_size: Set the the size register.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: the size of the following read transaction, in words.
+ **/
+static inline void fifo_icap_set_read_size(struct hwicap_drvdata *drvdata,
+		u32 data)
+{
+	out_be32(drvdata->base_address + XHI_SZ_OFFSET, data);
+}
+
+/**
+ * fifo_icap_start_config: Initiate a configuration (write) to the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline void fifo_icap_start_config(struct hwicap_drvdata *drvdata)
+{
+	out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_WRITE_MASK);
+	dev_dbg(drvdata->dev, "configuration started\n");
+}
+
+/**
+ * fifo_icap_start_readback: Initiate a readback from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata)
+{
+	out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_READ_MASK);
+	dev_dbg(drvdata->dev, "readback started\n");
+}
+
+/**
+ * fifo_icap_busy: Return true if the ICAP is still processing a transaction.
+ * @parameter drvdata: a pointer to the drvdata.
+ **/
+static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata)
+{
+	u32 status = in_be32(drvdata->base_address + XHI_SR_OFFSET);
+	dev_dbg(drvdata->dev, "Getting status = %x\n", status);
+	return (status & XHI_SR_DONE_MASK) ? 0 : 1;
+}
+
+/**
+ * fifo_icap_write_fifo_vacancy: Query the write fifo available space.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Return the number of words that can be safely pushed into the write fifo.
+ **/
+static inline u32 fifo_icap_write_fifo_vacancy(
+		struct hwicap_drvdata *drvdata)
+{
+	return in_be32(drvdata->base_address + XHI_WFV_OFFSET);
+}
+
+/**
+ * fifo_icap_read_fifo_occupancy: Query the read fifo available data.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * Return the number of words that can be safely read from the read fifo.
+ **/
+static inline u32 fifo_icap_read_fifo_occupancy(
+		struct hwicap_drvdata *drvdata)
+{
+	return in_be32(drvdata->base_address + XHI_RFO_OFFSET);
+}
+
+/**
+ * fifo_icap_set_configuration: Send configuration data to the ICAP.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter frame_buffer: a pointer to the data to be written to the
+ *		ICAP device.
+ * @parameter num_words: the number of words (32 bit) to write to the ICAP
+ *		device.
+
+ * This function writes the given user data to the Write FIFO in
+ * polled mode and starts the transfer of the data to
+ * the ICAP device.
+ **/
+int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata,
+		u32 *frame_buffer, u32 num_words)
+{
+
+	u32 write_fifo_vacancy = 0;
+	u32 retries = 0;
+	u32 remaining_words;
+
+	dev_dbg(drvdata->dev, "fifo_set_configuration\n");
+
+	/*
+	 * Check if the ICAP device is Busy with the last Read/Write
+	 */
+	if (fifo_icap_busy(drvdata))
+		return -EBUSY;
+
+	/*
+	 * Set up the buffer pointer and the words to be transferred.
+	 */
+	remaining_words = num_words;
+
+	while (remaining_words > 0) {
+		/*
+		 * Wait until we have some data in the fifo.
+		 */
+		while (write_fifo_vacancy == 0) {
+			write_fifo_vacancy =
+				fifo_icap_write_fifo_vacancy(drvdata);
+			retries++;
+			if (retries > XHI_MAX_RETRIES)
+				return -EIO;
+		}
+
+		/*
+		 * Write data into the Write FIFO.
+		 */
+		while ((write_fifo_vacancy != 0) &&
+				(remaining_words > 0)) {
+			fifo_icap_fifo_write(drvdata, *frame_buffer);
+
+			remaining_words--;
+			write_fifo_vacancy--;
+			frame_buffer++;
+		}
+		/* Start pushing whatever is in the FIFO into the ICAP. */
+		fifo_icap_start_config(drvdata);
+	}
+
+	/* Wait until the write has finished. */
+	while (fifo_icap_busy(drvdata)) {
+		retries++;
+		if (retries > XHI_MAX_RETRIES)
+			break;
+	}
+
+	dev_dbg(drvdata->dev, "done fifo_set_configuration\n");
+
+	/*
+	 * If the requested number of words have not been read from
+	 * the device then indicate failure.
+	 */
+	if (remaining_words != 0)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * fifo_icap_get_configuration: Read configuration data from the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter data: Address of the data representing the partial bitstream
+ * @parameter size: the size of the partial bitstream in 32 bit words.
+ *
+ * This function reads the specified number of words from the ICAP device in
+ * the polled mode.
+ */
+int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata,
+		u32 *frame_buffer, u32 num_words)
+{
+
+	u32 read_fifo_occupancy = 0;
+	u32 retries = 0;
+	u32 *data = frame_buffer;
+	u32 remaining_words;
+	u32 words_to_read;
+
+	dev_dbg(drvdata->dev, "fifo_get_configuration\n");
+
+	/*
+	 * Check if the ICAP device is Busy with the last Write/Read
+	 */
+	if (fifo_icap_busy(drvdata))
+		return -EBUSY;
+
+	remaining_words = num_words;
+
+	while (remaining_words > 0) {
+		words_to_read = remaining_words;
+		/* The hardware has a limit on the number of words
+		   that can be read at one time.  */
+		if (words_to_read > XHI_MAX_READ_TRANSACTION_WORDS)
+			words_to_read = XHI_MAX_READ_TRANSACTION_WORDS;
+
+		remaining_words -= words_to_read;
+
+		fifo_icap_set_read_size(drvdata, words_to_read);
+		fifo_icap_start_readback(drvdata);
+
+		while (words_to_read > 0) {
+			/* Wait until we have some data in the fifo. */
+			while (read_fifo_occupancy == 0) {
+				read_fifo_occupancy =
+					fifo_icap_read_fifo_occupancy(drvdata);
+				retries++;
+				if (retries > XHI_MAX_RETRIES)
+					return -EIO;
+			}
+
+			if (read_fifo_occupancy > words_to_read)
+				read_fifo_occupancy = words_to_read;
+
+			words_to_read -= read_fifo_occupancy;
+
+			/* Read the data from the Read FIFO. */
+			while (read_fifo_occupancy != 0) {
+				*data++ = fifo_icap_fifo_read(drvdata);
+				read_fifo_occupancy--;
+			}
+		}
+	}
+
+	dev_dbg(drvdata->dev, "done fifo_get_configuration\n");
+
+	return 0;
+}
+
+/**
+ * buffer_icap_reset: Reset the logic of the icap device.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This function forces the software reset of the complete HWICAP device.
+ * All the registers will return to the default value and the FIFO is also
+ * flushed as a part of this software reset.
+ */
+void fifo_icap_reset(struct hwicap_drvdata *drvdata)
+{
+	u32 reg_data;
+	/*
+	 * Reset the device by setting/clearing the RESET bit in the
+	 * Control Register.
+	 */
+	reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data | XHI_CR_SW_RESET_MASK);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data & (~XHI_CR_SW_RESET_MASK));
+
+}
+
+/**
+ * fifo_icap_flush_fifo: This function flushes the FIFOs in the device.
+ * @parameter drvdata: a pointer to the drvdata.
+ */
+void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata)
+{
+	u32 reg_data;
+	/*
+	 * Flush the FIFO by setting/clearing the FIFO Clear bit in the
+	 * Control Register.
+	 */
+	reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data | XHI_CR_FIFO_CLR_MASK);
+
+	out_be32(drvdata->base_address + XHI_CR_OFFSET,
+				reg_data & (~XHI_CR_FIFO_CLR_MASK));
+}
+
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h
new file mode 100644
index 0000000..4d3068d
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/fifo_icap.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_FIFO_ICAP_H_	/* prevent circular inclusions */
+#define XILINX_FIFO_ICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include "xilinx_hwicap.h"
+
+/* Reads integers from the device into the storage buffer. */
+int fifo_icap_get_configuration(
+		struct hwicap_drvdata *drvdata,
+		u32 *FrameBuffer,
+		u32 NumWords);
+
+/* Writes integers to the device from the storage buffer. */
+int fifo_icap_set_configuration(
+		struct hwicap_drvdata *drvdata,
+		u32 *FrameBuffer,
+		u32 NumWords);
+
+void fifo_icap_reset(struct hwicap_drvdata *drvdata);
+void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata);
+
+#endif
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
new file mode 100644
index 0000000..24f6aef
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -0,0 +1,904 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2002 Xilinx Inc., Systems Engineering Group
+ *     (c) Copyright 2004 Xilinx Inc., Systems Engineering Group
+ *     (c) Copyright 2007-2008 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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.
+ *
+ *****************************************************************************/
+
+/*
+ * This is the code behind /dev/xilinx_icap -- it allows a user-space
+ * application to use the Xilinx ICAP subsystem.
+ *
+ * The following operations are possible:
+ *
+ * open         open the port and initialize for access.
+ * release      release port
+ * write        Write a bitstream to the configuration processor.
+ * read         Read a data stream from the configuration processor.
+ *
+ * After being opened, the port is initialized and accessed to avoid a
+ * corrupted first read which may occur with some hardware.  The port
+ * is left in a desynched state, requiring that a synch sequence be
+ * transmitted before any valid configuration data.  A user will have
+ * exclusive access to the device while it remains open, and the state
+ * of the ICAP cannot be guaranteed after the device is closed.  Note
+ * that a complete reset of the core and the state of the ICAP cannot
+ * be performed on many versions of the cores, hence users of this
+ * device should avoid making inconsistent accesses to the device.  In
+ * particular, accessing the read interface, without first generating
+ * a write containing a readback packet can leave the ICAP in an
+ * inaccessible state.
+ *
+ * Note that in order to use the read interface, it is first necessary
+ * to write a request packet to the write interface.  i.e., it is not
+ * possible to simply readback the bitstream (or any configuration
+ * bits) from a device without specifically requesting them first.
+ * The code to craft such packets is intended to be part of the
+ * user-space application code that uses this device.  The simplest
+ * way to use this interface is simply:
+ *
+ * cp foo.bit /dev/xilinx_icap
+ *
+ * Note that unless foo.bit is an appropriately constructed partial
+ * bitstream, this has a high likelyhood of overwriting the design
+ * currently programmed in the FPGA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/sysctl.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_OF
+/* For open firmware. */
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#endif
+
+#include "xilinx_hwicap.h"
+#include "buffer_icap.h"
+#include "fifo_icap.h"
+
+#define DRIVER_NAME "xilinx_icap"
+
+#define HWICAP_REGS   (0x10000)
+
+/* dynamically allocate device number */
+static int xhwicap_major;
+static int xhwicap_minor;
+#define HWICAP_DEVICES 1
+
+module_param(xhwicap_major, int, S_IRUGO);
+module_param(xhwicap_minor, int, S_IRUGO);
+
+/* An array, which is set to true when the device is registered. */
+static bool probed_devices[HWICAP_DEVICES];
+
+static struct class *icap_class;
+
+#define UNIMPLEMENTED 0xFFFF
+
+static const struct config_registers v2_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = 11,
+	.KEY = 12,
+	.CBC = 13,
+	.IDCODE = 14,
+	.AXSS = UNIMPLEMENTED,
+	.C0R_1 = UNIMPLEMENTED,
+	.CSOB = UNIMPLEMENTED,
+	.WBSTAR = UNIMPLEMENTED,
+	.TIMER = UNIMPLEMENTED,
+	.BOOTSTS = UNIMPLEMENTED,
+	.CTL_1 = UNIMPLEMENTED,
+};
+
+static const struct config_registers v4_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = UNIMPLEMENTED,
+	.KEY = UNIMPLEMENTED,
+	.CBC = 11,
+	.IDCODE = 12,
+	.AXSS = 13,
+	.C0R_1 = UNIMPLEMENTED,
+	.CSOB = UNIMPLEMENTED,
+	.WBSTAR = UNIMPLEMENTED,
+	.TIMER = UNIMPLEMENTED,
+	.BOOTSTS = UNIMPLEMENTED,
+	.CTL_1 = UNIMPLEMENTED,
+};
+static const struct config_registers v5_config_registers = {
+	.CRC = 0,
+	.FAR = 1,
+	.FDRI = 2,
+	.FDRO = 3,
+	.CMD = 4,
+	.CTL = 5,
+	.MASK = 6,
+	.STAT = 7,
+	.LOUT = 8,
+	.COR = 9,
+	.MFWR = 10,
+	.FLR = UNIMPLEMENTED,
+	.KEY = UNIMPLEMENTED,
+	.CBC = 11,
+	.IDCODE = 12,
+	.AXSS = 13,
+	.C0R_1 = 14,
+	.CSOB = 15,
+	.WBSTAR = 16,
+	.TIMER = 17,
+	.BOOTSTS = 18,
+	.CTL_1 = 19,
+};
+
+/**
+ * hwicap_command_desync: Send a DESYNC command to the ICAP port.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This command desynchronizes the ICAP After this command, a
+ * bitstream containing a NULL packet, followed by a SYNCH packet is
+ * required before the ICAP will recognize commands.
+ */
+int hwicap_command_desync(struct hwicap_drvdata *drvdata)
+{
+	u32 buffer[4];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1;
+	buffer[index++] = XHI_CMD_DESYNCH;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data present
+	 * in the FIFO to the ICAP device.
+	 */
+	return drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+}
+
+/**
+ * hwicap_command_capture: Send a CAPTURE command to the ICAP port.
+ * @parameter drvdata: a pointer to the drvdata.
+ *
+ * This command captures all of the flip flop states so they will be
+ * available during readback.  One can use this command instead of
+ * enabling the CAPTURE block in the design.
+ */
+int hwicap_command_capture(struct hwicap_drvdata *drvdata)
+{
+	u32 buffer[7];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_SYNC_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1;
+	buffer[index++] = XHI_CMD_GCAPTURE;
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_DUMMY_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data
+	 * present in the FIFO to the ICAP device.
+	 */
+	return drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+
+}
+
+/**
+ * hwicap_get_configuration_register: Query a configuration register.
+ * @parameter drvdata: a pointer to the drvdata.
+ * @parameter reg: a constant which represents the configuration
+ *		register value to be returned.
+ * 		Examples:  XHI_IDCODE, XHI_FLR.
+ * @parameter RegData: returns the value of the register.
+ *
+ * Sends a query packet to the ICAP and then receives the response.
+ * The icap is left in Synched state.
+ */
+int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata,
+		u32 reg, u32 *RegData)
+{
+	int status;
+	u32 buffer[6];
+	u32 index = 0;
+
+	/*
+	 * Create the data to be written to the ICAP.
+	 */
+	buffer[index++] = XHI_DUMMY_PACKET;
+	buffer[index++] = XHI_SYNC_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = hwicap_type_1_read(reg) | 1;
+	buffer[index++] = XHI_NOOP_PACKET;
+	buffer[index++] = XHI_NOOP_PACKET;
+
+	/*
+	 * Write the data to the FIFO and intiate the transfer of data present
+	 * in the FIFO to the ICAP device.
+	 */
+	status = drvdata->config->set_configuration(drvdata,
+			&buffer[0], index);
+	if (status)
+		return status;
+
+	/*
+	 * Read the configuration register
+	 */
+	status = drvdata->config->get_configuration(drvdata, RegData, 1);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata)
+{
+	int status;
+	u32 idcode;
+
+	dev_dbg(drvdata->dev, "initializing\n");
+
+	/* Abort any current transaction, to make sure we have the
+	 * ICAP in a good state. */
+	dev_dbg(drvdata->dev, "Reset...\n");
+	drvdata->config->reset(drvdata);
+
+	dev_dbg(drvdata->dev, "Desync...\n");
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		return status;
+
+	/* Attempt to read the IDCODE from ICAP.  This
+	 * may not be returned correctly, due to the design of the
+	 * hardware.
+	 */
+	dev_dbg(drvdata->dev, "Reading IDCODE...\n");
+	status = hwicap_get_configuration_register(
+			drvdata, drvdata->config_regs->IDCODE, &idcode);
+	dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode);
+	if (status)
+		return status;
+
+	dev_dbg(drvdata->dev, "Desync...\n");
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+static ssize_t
+hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	ssize_t bytes_to_read = 0;
+	u32 *kbuf;
+	u32 words;
+	u32 bytes_remaining;
+	int status;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->read_buffer_in_use) {
+		/* If there are leftover bytes in the buffer, just */
+		/* return them and don't try to read more from the */
+		/* ICAP device. */
+		bytes_to_read =
+			(count < drvdata->read_buffer_in_use) ? count :
+			drvdata->read_buffer_in_use;
+
+		/* Return the data currently in the read buffer. */
+		if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) {
+			status = -EFAULT;
+			goto error;
+		}
+		drvdata->read_buffer_in_use -= bytes_to_read;
+		memcpy(drvdata->read_buffer + bytes_to_read,
+				drvdata->read_buffer, 4 - bytes_to_read);
+	} else {
+		/* Get new data from the ICAP, and return was was requested. */
+		kbuf = (u32 *) get_zeroed_page(GFP_KERNEL);
+		if (!kbuf) {
+			status = -ENOMEM;
+			goto error;
+		}
+
+		/* The ICAP device is only able to read complete */
+		/* words.  If a number of bytes that do not correspond */
+		/* to complete words is requested, then we read enough */
+		/* words to get the required number of bytes, and then */
+		/* save the remaining bytes for the next read. */
+
+		/* Determine the number of words to read, rounding up */
+		/* if necessary. */
+		words = ((count + 3) >> 2);
+		bytes_to_read = words << 2;
+
+		if (bytes_to_read > PAGE_SIZE)
+			bytes_to_read = PAGE_SIZE;
+
+		/* Ensure we only read a complete number of words. */
+		bytes_remaining = bytes_to_read & 3;
+		bytes_to_read &= ~3;
+		words = bytes_to_read >> 2;
+
+		status = drvdata->config->get_configuration(drvdata,
+				kbuf, words);
+
+		/* If we didn't read correctly, then bail out. */
+		if (status) {
+			free_page((unsigned long)kbuf);
+			goto error;
+		}
+
+		/* If we fail to return the data to the user, then bail out. */
+		if (copy_to_user(buf, kbuf, bytes_to_read)) {
+			free_page((unsigned long)kbuf);
+			status = -EFAULT;
+			goto error;
+		}
+		memcpy(kbuf, drvdata->read_buffer, bytes_remaining);
+		drvdata->read_buffer_in_use = bytes_remaining;
+		free_page((unsigned long)kbuf);
+	}
+	status = bytes_to_read;
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static ssize_t
+hwicap_write(struct file *file, const char *buf,
+		size_t count, loff_t *ppos)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	ssize_t written = 0;
+	ssize_t left = count;
+	u32 *kbuf;
+	ssize_t len;
+	ssize_t status;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	left += drvdata->write_buffer_in_use;
+
+	/* Only write multiples of 4 bytes. */
+	if (left < 4) {
+		status = 0;
+		goto error;
+	}
+
+	kbuf = (u32 *) __get_free_page(GFP_KERNEL);
+	if (!kbuf) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	while (left > 3) {
+		/* only write multiples of 4 bytes, so there might */
+		/* be as many as 3 bytes left (at the end). */
+		len = left;
+
+		if (len > PAGE_SIZE)
+			len = PAGE_SIZE;
+		len &= ~3;
+
+		if (drvdata->write_buffer_in_use) {
+			memcpy(kbuf, drvdata->write_buffer,
+					drvdata->write_buffer_in_use);
+			if (copy_from_user(
+			    (((char *)kbuf) + (drvdata->write_buffer_in_use)),
+			    buf + written,
+			    len - (drvdata->write_buffer_in_use))) {
+				free_page((unsigned long)kbuf);
+				status = -EFAULT;
+				goto error;
+			}
+		} else {
+			if (copy_from_user(kbuf, buf + written, len)) {
+				free_page((unsigned long)kbuf);
+				status = -EFAULT;
+				goto error;
+			}
+		}
+
+		status = drvdata->config->set_configuration(drvdata,
+				kbuf, len >> 2);
+
+		if (status) {
+			free_page((unsigned long)kbuf);
+			status = -EFAULT;
+			goto error;
+		}
+		if (drvdata->write_buffer_in_use) {
+			len -= drvdata->write_buffer_in_use;
+			left -= drvdata->write_buffer_in_use;
+			drvdata->write_buffer_in_use = 0;
+		}
+		written += len;
+		left -= len;
+	}
+	if ((left > 0) && (left < 4)) {
+		if (!copy_from_user(drvdata->write_buffer,
+						buf + written, left)) {
+			drvdata->write_buffer_in_use = left;
+			written += left;
+			left = 0;
+		}
+	}
+
+	free_page((unsigned long)kbuf);
+	status = written;
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static int hwicap_open(struct inode *inode, struct file *file)
+{
+	struct hwicap_drvdata *drvdata;
+	int status;
+
+	drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev);
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->is_open) {
+		status = -EBUSY;
+		goto error;
+	}
+
+	status = hwicap_initialize_hwicap(drvdata);
+	if (status) {
+		dev_err(drvdata->dev, "Failed to open file");
+		goto error;
+	}
+
+	file->private_data = drvdata;
+	drvdata->write_buffer_in_use = 0;
+	drvdata->read_buffer_in_use = 0;
+	drvdata->is_open = 1;
+
+ error:
+	up(&drvdata->sem);
+	return status;
+}
+
+static int hwicap_release(struct inode *inode, struct file *file)
+{
+	struct hwicap_drvdata *drvdata = file->private_data;
+	int i;
+	int status = 0;
+
+	if (down_interruptible(&drvdata->sem))
+		return -ERESTARTSYS;
+
+	if (drvdata->write_buffer_in_use) {
+		/* Flush write buffer. */
+		for (i = drvdata->write_buffer_in_use; i < 4; i++)
+			drvdata->write_buffer[i] = 0;
+
+		status = drvdata->config->set_configuration(drvdata,
+				(u32 *) drvdata->write_buffer, 1);
+		if (status)
+			goto error;
+	}
+
+	status = hwicap_command_desync(drvdata);
+	if (status)
+		goto error;
+
+ error:
+	drvdata->is_open = 0;
+	up(&drvdata->sem);
+	return status;
+}
+
+static struct file_operations hwicap_fops = {
+	.owner = THIS_MODULE,
+	.write = hwicap_write,
+	.read = hwicap_read,
+	.open = hwicap_open,
+	.release = hwicap_release,
+};
+
+static int __devinit hwicap_setup(struct device *dev, int id,
+		const struct resource *regs_res,
+		const struct hwicap_driver_config *config,
+		const struct config_registers *config_regs)
+{
+	dev_t devt;
+	struct hwicap_drvdata *drvdata = NULL;
+	int retval = 0;
+
+	dev_info(dev, "Xilinx icap port driver\n");
+
+	if (id < 0) {
+		for (id = 0; id < HWICAP_DEVICES; id++)
+			if (!probed_devices[id])
+				break;
+	}
+	if (id < 0 || id >= HWICAP_DEVICES) {
+		dev_err(dev, "%s%i too large\n", DRIVER_NAME, id);
+		return -EINVAL;
+	}
+	if (probed_devices[id]) {
+		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
+			DRIVER_NAME, id);
+		return -EBUSY;
+	}
+
+	probed_devices[id] = 1;
+
+	devt = MKDEV(xhwicap_major, xhwicap_minor + id);
+
+	drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL);
+	if (!drvdata) {
+		dev_err(dev, "Couldn't allocate device private record\n");
+		return -ENOMEM;
+	}
+	memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata));
+	dev_set_drvdata(dev, (void *)drvdata);
+
+	if (!regs_res) {
+		dev_err(dev, "Couldn't get registers resource\n");
+		retval = -EFAULT;
+		goto failed1;
+	}
+
+	drvdata->mem_start = regs_res->start;
+	drvdata->mem_end = regs_res->end;
+	drvdata->mem_size = regs_res->end - regs_res->start + 1;
+
+	if (!request_mem_region(drvdata->mem_start,
+					drvdata->mem_size, DRIVER_NAME)) {
+		dev_err(dev, "Couldn't lock memory region at %p\n",
+			(void *)regs_res->start);
+		retval = -EBUSY;
+		goto failed1;
+	}
+
+	drvdata->devt = devt;
+	drvdata->dev = dev;
+	drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size);
+	if (!drvdata->base_address) {
+		dev_err(dev, "ioremap() failed\n");
+		goto failed2;
+	}
+
+	drvdata->config = config;
+	drvdata->config_regs = config_regs;
+
+	init_MUTEX(&drvdata->sem);
+	drvdata->is_open = 0;
+
+	dev_info(dev, "ioremap %lx to %p with size %x\n",
+		 (unsigned long int)drvdata->mem_start,
+			drvdata->base_address, drvdata->mem_size);
+
+	cdev_init(&drvdata->cdev, &hwicap_fops);
+	drvdata->cdev.owner = THIS_MODULE;
+	retval = cdev_add(&drvdata->cdev, devt, 1);
+	if (retval) {
+		dev_err(dev, "cdev_add() failed\n");
+		goto failed3;
+	}
+	/*  devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */
+	class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME);
+	return 0;		/* success */
+
+ failed3:
+	iounmap(drvdata->base_address);
+
+ failed2:
+	release_mem_region(regs_res->start, drvdata->mem_size);
+
+ failed1:
+	kfree(drvdata);
+
+	return retval;
+}
+
+static struct hwicap_driver_config buffer_icap_config = {
+	.get_configuration = buffer_icap_get_configuration,
+	.set_configuration = buffer_icap_set_configuration,
+	.reset = buffer_icap_reset,
+};
+
+static struct hwicap_driver_config fifo_icap_config = {
+	.get_configuration = fifo_icap_get_configuration,
+	.set_configuration = fifo_icap_set_configuration,
+	.reset = fifo_icap_reset,
+};
+
+static int __devexit hwicap_remove(struct device *dev)
+{
+	struct hwicap_drvdata *drvdata;
+
+	drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
+
+	if (!drvdata)
+		return 0;
+
+	class_device_destroy(icap_class, drvdata->devt);
+	cdev_del(&drvdata->cdev);
+	iounmap(drvdata->base_address);
+	release_mem_region(drvdata->mem_start, drvdata->mem_size);
+	kfree(drvdata);
+	dev_set_drvdata(dev, NULL);
+	probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0;
+
+	return 0;		/* success */
+}
+
+static int __devinit hwicap_drv_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	const struct config_registers *regs;
+	const char *family;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	/* It's most likely that we're using V4, if the family is not
+	   specified */
+	regs = &v4_config_registers;
+	family = pdev->dev.platform_data;
+
+	if (family) {
+		if (!strcmp(family, "virtex2p")) {
+			regs = &v2_config_registers;
+		} else if (!strcmp(family, "virtex4")) {
+			regs = &v4_config_registers;
+		} else if (!strcmp(family, "virtex5")) {
+			regs = &v5_config_registers;
+		}
+	}
+
+	return hwicap_setup(&pdev->dev, pdev->id, res,
+			&buffer_icap_config, regs);
+}
+
+static int __devexit hwicap_drv_remove(struct platform_device *pdev)
+{
+	return hwicap_remove(&pdev->dev);
+}
+
+static struct platform_driver hwicap_platform_driver = {
+	.probe = hwicap_drv_probe,
+	.remove = hwicap_drv_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * OF bus binding
+ */
+
+#if defined(CONFIG_OF)
+static int __devinit
+hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
+{
+	struct resource res;
+	const unsigned int *id;
+	const char *family;
+	int rc;
+	const struct hwicap_driver_config *config = match->data;
+	const struct config_registers *regs;
+
+	dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match);
+
+	rc = of_address_to_resource(op->node, 0, &res);
+	if (rc) {
+		dev_err(&op->dev, "invalid address\n");
+		return rc;
+	}
+
+	id = of_get_property(op->node, "port-number", NULL);
+
+	/* It's most likely that we're using V4, if the family is not
+	   specified */
+	regs = &v4_config_registers;
+	family = of_get_property(op->node, "xlnx,family", NULL);
+
+	if (family) {
+		if (!strcmp(family, "virtex2p")) {
+			regs = &v2_config_registers;
+		} else if (!strcmp(family, "virtex4")) {
+			regs = &v4_config_registers;
+		} else if (!strcmp(family, "virtex5")) {
+			regs = &v5_config_registers;
+		}
+	}
+	return hwicap_setup(&op->dev, id ? *id : -1, &res, config,
+			regs);
+}
+
+static int __devexit hwicap_of_remove(struct of_device *op)
+{
+	return hwicap_remove(&op->dev);
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id __devinit hwicap_of_match[] = {
+	{ .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config},
+	{ .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hwicap_of_match);
+
+static struct of_platform_driver hwicap_of_driver = {
+	.owner = THIS_MODULE,
+	.name = DRIVER_NAME,
+	.match_table = hwicap_of_match,
+	.probe = hwicap_of_probe,
+	.remove = __devexit_p(hwicap_of_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+};
+
+/* Registration helpers to keep the number of #ifdefs to a minimum */
+static inline int __devinit hwicap_of_register(void)
+{
+	pr_debug("hwicap: calling of_register_platform_driver()\n");
+	return of_register_platform_driver(&hwicap_of_driver);
+}
+
+static inline void __devexit hwicap_of_unregister(void)
+{
+	of_unregister_platform_driver(&hwicap_of_driver);
+}
+#else /* CONFIG_OF */
+/* CONFIG_OF not enabled; do nothing helpers */
+static inline int __devinit hwicap_of_register(void) { return 0; }
+static inline void __devexit hwicap_of_unregister(void) { }
+#endif /* CONFIG_OF */
+
+static int __devinit hwicap_module_init(void)
+{
+	dev_t devt;
+	int retval;
+
+	icap_class = class_create(THIS_MODULE, "xilinx_config");
+
+	if (xhwicap_major) {
+		devt = MKDEV(xhwicap_major, xhwicap_minor);
+		retval = register_chrdev_region(
+				devt,
+				HWICAP_DEVICES,
+				DRIVER_NAME);
+		if (retval < 0)
+			return retval;
+	} else {
+		retval = alloc_chrdev_region(&devt,
+				xhwicap_minor,
+				HWICAP_DEVICES,
+				DRIVER_NAME);
+		if (retval < 0)
+			return retval;
+		xhwicap_major = MAJOR(devt);
+	}
+
+	retval = platform_driver_register(&hwicap_platform_driver);
+
+	if (retval)
+		goto failed1;
+
+	retval = hwicap_of_register();
+
+	if (retval)
+		goto failed2;
+
+	return retval;
+
+ failed2:
+	platform_driver_unregister(&hwicap_platform_driver);
+
+ failed1:
+	unregister_chrdev_region(devt, HWICAP_DEVICES);
+
+	return retval;
+}
+
+static void __devexit hwicap_module_cleanup(void)
+{
+	dev_t devt = MKDEV(xhwicap_major, xhwicap_minor);
+
+	class_destroy(icap_class);
+
+	platform_driver_unregister(&hwicap_platform_driver);
+
+	hwicap_of_unregister();
+
+	unregister_chrdev_region(devt, HWICAP_DEVICES);
+}
+
+module_init(hwicap_module_init);
+module_exit(hwicap_module_cleanup);
+
+MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group");
+MODULE_DESCRIPTION("Xilinx ICAP Port Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
new file mode 100644
index 0000000..b6b47d0
--- /dev/null
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
@@ -0,0 +1,193 @@
+/*****************************************************************************
+ *
+ *     Author: Xilinx, Inc.
+ *
+ *     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.
+ *
+ *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
+ *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
+ *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
+ *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
+ *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
+ *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
+ *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
+ *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
+ *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
+ *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
+ *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
+ *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *     FOR A PARTICULAR PURPOSE.
+ *
+ *     Xilinx products are not intended for use in life support appliances,
+ *     devices, or systems. Use in such applications is expressly prohibited.
+ *
+ *     (c) Copyright 2003-2007 Xilinx Inc.
+ *     All rights reserved.
+ *
+ *     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 XILINX_HWICAP_H_	/* prevent circular inclusions */
+#define XILINX_HWICAP_H_	/* by using protection macros */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+struct hwicap_drvdata {
+	u32 write_buffer_in_use;  /* Always in [0,3] */
+	u8 write_buffer[4];
+	u32 read_buffer_in_use;	  /* Always in [0,3] */
+	u8 read_buffer[4];
+	u32 mem_start;		  /* phys. address of the control registers */
+	u32 mem_end;		  /* phys. address of the control registers */
+	u32 mem_size;
+	void __iomem *base_address;/* virt. address of the control registers */
+
+	struct device *dev;
+	struct cdev cdev;	/* Char device structure */
+	dev_t devt;
+
+	const struct hwicap_driver_config *config;
+	const struct config_registers *config_regs;
+	void *private_data;
+	bool is_open;
+	bool is_accessing;
+};
+
+struct hwicap_driver_config {
+	int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data,
+			u32 size);
+	int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data,
+			u32 size);
+	void (*reset)(struct hwicap_drvdata *drvdata);
+};
+
+/* Number of times to poll the done regsiter */
+#define XHI_MAX_RETRIES     10
+
+/************ Constant Definitions *************/
+
+#define XHI_PAD_FRAMES              0x1
+
+/* Mask for calculating configuration packet headers */
+#define XHI_WORD_COUNT_MASK_TYPE_1  0x7FFUL
+#define XHI_WORD_COUNT_MASK_TYPE_2  0x1FFFFFUL
+#define XHI_TYPE_MASK               0x7
+#define XHI_REGISTER_MASK           0xF
+#define XHI_OP_MASK                 0x3
+
+#define XHI_TYPE_SHIFT              29
+#define XHI_REGISTER_SHIFT          13
+#define XHI_OP_SHIFT                27
+
+#define XHI_TYPE_1                  1
+#define XHI_TYPE_2                  2
+#define XHI_OP_WRITE                2
+#define XHI_OP_READ                 1
+
+/* Address Block Types */
+#define XHI_FAR_CLB_BLOCK           0
+#define XHI_FAR_BRAM_BLOCK          1
+#define XHI_FAR_BRAM_INT_BLOCK      2
+
+struct config_registers {
+	u32 CRC;
+	u32 FAR;
+	u32 FDRI;
+	u32 FDRO;
+	u32 CMD;
+	u32 CTL;
+	u32 MASK;
+	u32 STAT;
+	u32 LOUT;
+	u32 COR;
+	u32 MFWR;
+	u32 FLR;
+	u32 KEY;
+	u32 CBC;
+	u32 IDCODE;
+	u32 AXSS;
+	u32 C0R_1;
+	u32 CSOB;
+	u32 WBSTAR;
+	u32 TIMER;
+	u32 BOOTSTS;
+	u32 CTL_1;
+};
+
+/* Configuration Commands */
+#define XHI_CMD_NULL                0
+#define XHI_CMD_WCFG                1
+#define XHI_CMD_MFW                 2
+#define XHI_CMD_DGHIGH              3
+#define XHI_CMD_RCFG                4
+#define XHI_CMD_START               5
+#define XHI_CMD_RCAP                6
+#define XHI_CMD_RCRC                7
+#define XHI_CMD_AGHIGH              8
+#define XHI_CMD_SWITCH              9
+#define XHI_CMD_GRESTORE            10
+#define XHI_CMD_SHUTDOWN            11
+#define XHI_CMD_GCAPTURE            12
+#define XHI_CMD_DESYNCH             13
+#define XHI_CMD_IPROG               15 /* Only in Virtex5 */
+#define XHI_CMD_CRCC                16 /* Only in Virtex5 */
+#define XHI_CMD_LTIMER              17 /* Only in Virtex5 */
+
+/* Packet constants */
+#define XHI_SYNC_PACKET             0xAA995566UL
+#define XHI_DUMMY_PACKET            0xFFFFFFFFUL
+#define XHI_NOOP_PACKET             (XHI_TYPE_1 << XHI_TYPE_SHIFT)
+#define XHI_TYPE_2_READ ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \
+			(XHI_OP_READ << XHI_OP_SHIFT))
+
+#define XHI_TYPE_2_WRITE ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \
+			(XHI_OP_WRITE << XHI_OP_SHIFT))
+
+#define XHI_TYPE2_CNT_MASK          0x07FFFFFF
+
+#define XHI_TYPE_1_PACKET_MAX_WORDS 2047UL
+#define XHI_TYPE_1_HEADER_BYTES     4
+#define XHI_TYPE_2_HEADER_BYTES     8
+
+/* Constant to use for CRC check when CRC has been disabled */
+#define XHI_DISABLED_AUTO_CRC       0x0000DEFCUL
+
+/**
+ * hwicap_type_1_read: Generates a Type 1 read packet header.
+ * @parameter: Register is the address of the register to be read back.
+ *
+ * Generates a Type 1 read packet header, which is used to indirectly
+ * read registers in the configuration logic.  This packet must then
+ * be sent through the icap device, and a return packet received with
+ * the information.
+ **/
+static inline u32 hwicap_type_1_read(u32 Register)
+{
+	return (XHI_TYPE_1 << XHI_TYPE_SHIFT) |
+		(Register << XHI_REGISTER_SHIFT) |
+		(XHI_OP_READ << XHI_OP_SHIFT);
+}
+
+/**
+ * hwicap_type_1_write: Generates a Type 1 write packet header
+ * @parameter: Register is the address of the register to be read back.
+ **/
+static inline u32 hwicap_type_1_write(u32 Register)
+{
+	return (XHI_TYPE_1 << XHI_TYPE_SHIFT) |
+		(Register << XHI_REGISTER_SHIFT) |
+		(XHI_OP_WRITE << XHI_OP_SHIFT);
+}
+
+#endif
-- 
1.5.3.4-dirty

^ permalink raw reply related

* Re: [PATCH] [POWERPC] fsl_soc: add support for "fsl, immr" compatible matching
From: Scott Wood @ 2008-02-01 21:16 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev
In-Reply-To: <20080201150117.GA26545@localhost.localdomain>

Anton Vorontsov wrote:
> -	soc8540@e0000000 {
> +	soc@e0000000 {
>  		#address-cells = <1>;
>  		#size-cells = <1>;
>  		#interrupt-cells = <2>;
> -		device_type = "soc";
> +		compatible = "fsl,mpc8540-immr", "fsl,immr", "simple-bus";
>  		ranges = <00000000 e0000000 00100000>
>  		reg = <e0000000 00003000>;
>  		bus-frequency = <0>;

It's called "CCSR" rather than "IMMR" on 85xx.

-Scott

^ permalink raw reply

* RE: 83xx immap_qe.h -> SIR type def error?
From: Russell McGuire @ 2008-02-01 21:37 UTC (permalink / raw)
  To: 'Kumar Gala'; +Cc: linuxppc-embedded
In-Reply-To: <1ADE91E0-282D-4455-9754-886846B3EB90@kernel.crashing.org>

I should state, I am looking at the MPC8360ERM.pdf Rev 2.0

-Russ

> -----Original Message-----
> From: Russell McGuire [mailto:rmcguire@videopresence.com]
> Sent: Friday, February 01, 2008 1:03 PM
> To: 'Kumar Gala'
> Cc: 'linuxppc-embedded@ozlabs.org'
> Subject: RE: 83xx immap_qe.h -> SIR type def error?
> 
> Kumar,
> 
> Yes in the main memeory map they are just listed as 1K RAM blocks.
> However, in the UM Section 36.6.1 <pg 36-12 or pg 1728 in the PDF>.
> 
> It gives the breakout for the RAM, which clearly indicates 16 bit fields.
> <Here is a short clip from Figure 36-8>
> 
> Access: Read/Write
> 0 1 2 3 4 5 6 7 10 11 13 14 15
> MCC SWTR SSEL 1 SSEL 2 SSEL 3 SSEL 4 SGS CSEL CNT BYT LST
> Figure 36-8. SI RAM Entry for UCC
> 
> Honest, mistake as if I were writing the header file I'd not have time to
> ready all 2000+ pages of the UM. We find these only as somebody goes in an
> tries to use them.
> And I am guessing not a lot of customers use the SI block.
> 
> -Russ
> > -----Original Message-----
> > From: Kumar Gala [mailto:galak@kernel.crashing.org]
> > Sent: Friday, February 01, 2008 6:56 AM
> > To: rmcguire@videopresence.com
> > Cc: linuxppc-embedded@ozlabs.org
> > Subject: Re: 83xx immap_qe.h -> SIR type def error?
> >
> >
> > On Feb 1, 2008, at 5:47 AM, Russell McGuire wrote:
> >
> > > All Freescale,
> > >
> > > Not sure if this is the place to post this, but I have run across
> > > what I
> > > consider to be a possible type error in the immap_qe.h file, for the
> > > asm/powerpc branch.
> > >
> > > In the file immap_qe.h
> > >
> > > /* SI Routing Tables */
> > > struct sir {
> > > 	u8  tx[0x400];
> > > 	u8  rx[0x400];
> > > 	u8  res0[0x800];
> > > }
> > >
> > > Shouldn't these types be defined as __be16 ?
> > >
> > > According to the Freescale manual this is a 16 bit field, not an 8-bit
> > > field.
> > >
> > > Spent an hour trying to figure out why I couldn't fill this field
> > > out with
> > > upper 8 bits last night.
> > >
> > > Thoughts?
> >
> > I'm guessing it was done this way since they are just looked as base
> > offsets.  Where in the UM do you see anything about them being 16-bit
> > quantities?  (I'm really know little about this).
> >
> > - k

^ permalink raw reply


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