LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] Add support for Designware SATA controller driver
From: Stephen Rothwell @ 2009-04-30  4:30 UTC (permalink / raw)
  To: Feng Kan; +Cc: linuxppc-dev
In-Reply-To: <1241041345-19479-1-git-send-email-fkan@amcc.com>

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

On Wed, 29 Apr 2009 14:42:25 -0700 Feng Kan <fkan@amcc.com> wrote:
>
> Signed-off-by: Feng Kan <fkan@amcc.com>
> ---
>  drivers/ata/Kconfig    |   76 +-
>  drivers/ata/Makefile   |    1 +
>  drivers/ata/sata_dwc.c | 2047 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 2091 insertions(+), 33 deletions(-)
>  create mode 100644 drivers/ata/sata_dwc.c
> 
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index 0bcf264..5321e47 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -72,56 +72,66 @@ config SATA_FSL
>  
>  	  If unsure, say N.
>  
> -config ATA_SFF
> -	bool "ATA SFF support"
> -	default y
> +config SATA_DWC
> +	tristate "DesignWare Cores SATA support"
> +	depends on 460EX
>  	help
> -	  This option adds support for ATA controllers with SFF
> -	  compliant or similar programming interface.
> +	  This option enables support for the Synopsys DesignWare Cores SATA
> +	  controller.
> +	  It can be found on the AMCC 460EX.
>  
> -	  SFF is the legacy IDE interface that has been around since
> -	  the dawn of time.  Almost all PATA controllers have an
> -	  SFF interface.  Many SATA controllers have an SFF interface
> -	  when configured into a legacy compatibility mode.
> +	  If unsure, say N.
>  
> -	  For users with exclusively modern controllers like AHCI,
> -	  Silicon Image 3124, or Marvell 6440, you may choose to
> -	  disable this uneeded SFF support.
> +config ATA_SFF
> +bool "ATA SFF support"
> +default y
> +help
> +  This option adds support for ATA controllers with SFF
> +  compliant or similar programming interface.

You seem to have reformatted some of this file (hopefully)
unintentionally ...

And the linuxppc-embedded mailing list has gone away.
-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: Next April 28: boot failure on PowerPC with SLQB
From: Nick Piggin @ 2009-04-30  4:11 UTC (permalink / raw)
  To: Sachin Sant
  Cc: Stephen Rothwell, Christoph Lameter, linux-kernel, linuxppc-dev,
	Pekka Enberg, linux-next
In-Reply-To: <49F87FAB.9050408@in.ibm.com>

On Wed, Apr 29, 2009 at 09:56:19PM +0530, Sachin Sant wrote:
> Nick Piggin wrote:
> >Does this help?
> >---
> With the patch the machine boots past the failure point, but panics
> immediately with the following trace...

OK good, that solves one problem.

 
> Unable to handle kernel paging request for data at address 0x00000010
> Faulting instruction address: 0xc0000000007d03ec
> Oops: Kernel access of bad area, sig: 11 [#1]
> SMP NR_CPUS=1024 DEBUG_PAGEALLOC NUMA pSeries
> Modules linked in:
> NIP: c0000000007d03ec LR: c0000000007b0bbc CTR: 0000000000136f8c
> REGS: c000000000a23bd0 TRAP: 0300   Not tainted  (2.6.30-rc3-next-20090429)
> MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 28000084  XER: 00000010
> DAR: 0000000000000010, DSISR: 0000000040000000
> TASK = c000000000955fc0[0] 'swapper' THREAD: c000000000a20000 CPU: 0
> GPR00: 0000000000000001 c000000000a23e50 c000000000a17650 000000000000001f
> GPR04: 0000000000000000 ffffffffffffffff 000000000077a4b9 800000000c9b2cc0
> GPR08: 0000000000000000 0000000000000010 0000000000000000 c00000000095b0f8
> GPR12: 0000000028000082 c000000000af2400 c0000000007f3200 c000000000705c32
> GPR16: 00000000014f3138 0000000000000000 c0000000007f3138 0000000002f1fc90
> GPR20: c0000000007f3150 c000000000725d11 00000000007bb8e4 0000000002f1fc90
> GPR24: 0000000002f1fc90 c0000000007f31f0 0000000000d00000 c000000000b73b10
> GPR28: c0000000007f0440 c00000000095db00 c00000000098d5f0 0000000003c90000
> NIP [c0000000007d03ec] .pidmap_init+0x28/0x88

Well kmalloc is failing. It should not be though, even if the
current node is offline, it should be able to fall back to other
nodes. Stephen's trace indicates the same thing.

Could you try the following patch please, and capture the output
it generates?

Thanks,
Nick
---
 mm/slqb.c |   40 ++++++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 6 deletions(-)

Index: linux-2.6/mm/slqb.c
===================================================================
--- linux-2.6.orig/mm/slqb.c
+++ linux-2.6/mm/slqb.c
@@ -1456,7 +1456,7 @@ static void *__remote_slab_alloc_node(st
 }
 
 static noinline void *__remote_slab_alloc(struct kmem_cache *s,
-				gfp_t gfpflags, int node)
+				gfp_t gfpflags, int node, int trace)
 {
 	void *object;
 	struct zonelist *zonelist;
@@ -1465,19 +1465,32 @@ static noinline void *__remote_slab_allo
 	enum zone_type high_zoneidx = gfp_zone(gfpflags);
 
 	object = __remote_slab_alloc_node(s, gfpflags, node);
+	if (trace && !object)
+		printk("__remote_slab_alloc_node(node:%d) failed\n", node);
 	if (likely(object || (gfpflags & __GFP_THISNODE)))
 		return object;
 
-	zonelist = node_zonelist(slab_node(current->mempolicy), gfpflags);
+	node = slab_node(current->mempolicy);
+	if (trace)
+		printk("slab_node(current->mempolicy) = %d\n", node);
+
+	zonelist = node_zonelist(node, gfpflags);
 	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
-		if (!cpuset_zone_allowed_hardwall(zone, gfpflags))
+		if (!cpuset_zone_allowed_hardwall(zone, gfpflags)) {
+			if (trace)
+				printk("cpuset not allowed node:%d\n", zone_to_nid(zone));
 			continue;
+		}
 
 		node = zone_to_nid(zone);
 		object = __remote_slab_alloc_node(s, gfpflags, node);
 		if (likely(object))
 			return object;
+		if (trace)
+			printk("__remote_slab_alloc_node(node:%d) failed\n", node);
 	}
+	if (trace)
+		printk("__remote_slab_alloc failed\n");
 	return NULL;
 }
 #endif
@@ -1488,7 +1501,7 @@ static noinline void *__remote_slab_allo
  * Must be called with interrupts disabled.
  */
 static __always_inline void *__slab_alloc(struct kmem_cache *s,
-				gfp_t gfpflags, int node)
+				gfp_t gfpflags, int node, int trace)
 {
 	void *object;
 	struct kmem_cache_cpu *c;
@@ -1497,7 +1510,7 @@ static __always_inline void *__slab_allo
 #ifdef CONFIG_NUMA
 	if (unlikely(node != -1) && unlikely(node != numa_node_id())) {
 try_remote:
-		return __remote_slab_alloc(s, gfpflags, node);
+		return __remote_slab_alloc(s, gfpflags, node, trace);
 	}
 #endif
 
@@ -1509,6 +1522,8 @@ try_remote:
 		object = cache_list_get_page(s, l);
 		if (unlikely(!object)) {
 			object = __slab_alloc_page(s, gfpflags, node);
+			if (trace && !object)
+				printk("__slab_alloc_page(node:%d) failed\n", node);
 #ifdef CONFIG_NUMA
 			if (unlikely(!object)) {
 				node = numa_node_id();
@@ -1532,10 +1547,11 @@ static __always_inline void *slab_alloc(
 {
 	void *object;
 	unsigned long flags;
+	int trace = 0;
 
 again:
 	local_irq_save(flags);
-	object = __slab_alloc(s, gfpflags, node);
+	object = __slab_alloc(s, gfpflags, node, trace);
 	local_irq_restore(flags);
 
 	if (unlikely(slab_debug(s)) && likely(object)) {
@@ -1546,6 +1562,18 @@ again:
 	if (unlikely(gfpflags & __GFP_ZERO) && likely(object))
 		memset(object, 0, s->objsize);
 
+	if (!object && !trace) {
+		trace = 1;
+		dump_stack();
+		printk("slab_alloc allocation failed\n");
+		printk("slab:%s flags:%x node:%d\n", s->name, gfpflags, node);
+		goto again;
+	}
+	if (trace) {
+		if (object)
+			printk("slab_alloc allocation worked when being traced, bugger\n");
+	}
+
 	return object;
 }
 

^ permalink raw reply

* Powerpc Port linux-2.6.29
From: Deepak Gaur @ 2009-04-30  3:30 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20090429110648.M98089@cdotd.ernet.in>

Hello all

I have a custom board with MPC8560 and 1GB DDR SDRAM. I am trying to port linux - 2.6.29
on this board. The U-BOOT is already ported on the board and is working.

The OS uImage is stored in code flash at address 0xe0000000 and OS is booted by U-BOOT 
While OS do boot but after some prints weired errors start showing up. Rootfile system
is NFS based . I have checked the register values with Hardware person using Trace32
emulator and they are as expected.

----------------------------------------------------------------------------------------
FLASH: 32.5 MB
L2:    256 KB enabled
In:    serial
Out:   serial
Err:   serial
Net:   TSEC1, TSEC2
Hit any key to stop autoboot:  0
WARNING: adjusting available memory to 30000000
## Booting kernel from Legacy Image at e0000000 ...
   Image Name:   Linux-2.6.29
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1451400 Bytes =  1.4 MB
   Load Address: 00400000
   Entry Point:  004005a8
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
Using MPC85xx ADS machine description
Memory CAM mapping: CAM0=256Mb, CAM1=256Mb, CAM2=256Mb residual: 256Mb
Linux version 2.6.29 (deepak@Localhost) (gcc version 3.4.3 (MontaVista
3.4.3-25.0.96.0600665 2006-05-179Found FSL PCI host bridge at 0x00000000a0008000.
Firmware bus number: 0->0
PCI host bridge /pci@a0008000 (primary) ranges:
 MEM 0x0000000080000000..0x000000009fffffff -> 0x0000000080000000
  IO 0x00000000a2000000..0x00000000a2ffffff -> 0x0000000000000000
Zone PFN ranges:
  DMA      0x00000000 -> 0x00030000
  Normal   0x00030000 -> 0x00030000
Movable zone start PFN for each node
early_node_map[1] active PFN ranges
    0: 0x00000000 -> 0x00030000
MMU: Allocated 1088 bytes of context maps for 255 contexts
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 195072
Kernel command line: console=ttyCPM0,9600 root=/dev/nfs rw nfsroot=rootpath ip=dhcp
mpic: Setting up MPIC " OpenPIC  " version 1.2 at a0040000, max 1 CPUs
mpic: ISU size: 56, shift: 6, mask: 3f
mpic: Initializing for 56 sources
PID hash table entries: 4096 (order: 12, 16384 bytes)
clocksource: timebase mult[79364d9] shift[22] registered
console [ttyCPM0] enabled
Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
BUG: Bad page state in process swapper  pfn:2fc69
page:c0d0ed20 flags:(null) count:0 mapcount:-524288 mapping:(null) index:0
Call Trace:
[c02dbef0] [c0007a84] show_stack+0x48/0x174 (unreliable)
[c02dbf20] [c006fa14] bad_page+0x9c/0x160
[c02dbf40] [c0071564] __free_pages_ok+0x2c8/0x3a8
[c02dbf70] [c02a9788] free_all_bootmem_core+0x1a4/0x240
[c02dbfa0] [c02a4544] mem_init+0x64/0x178
[c02dbfc0] [c029a67c] start_kernel+0x1d0/0x2a8
[c02dbff0] [c00003b4] skpinv+0x2dc/0x318
BUG: Bad page state in process swapper  pfn:2fc8f
page:c0d0f1e0 flags:(null) count:0 mapcount:7799008 mapping:(null) index:0
Call Trace:
[c02dbef0] [c0007a84] show_stack+0x48/0x174 (unreliable)
[c02dbf20] [c006fa14] bad_page+0x9c/0x160
[c02dbf40] [c0071564] __free_pages_ok+0x2c8/0x3a8
[c02dbf70] [c02a9788] free_all_bootmem_core+0x1a4/0x240
[c02dbfa0] [c02a4544] mem_init+0x64/0x178
[c02dbfc0] [c029a67c] start_kernel+0x1d0/0x2a8
[c02dbff0] [c00003b4] skpinv+0x2dc/0x318
BUG: Bad page state in process swapper  pfn:2fcc4
page:c0d0f880 flags:(null) count:0 mapcount:7799040 mapping:(null) index:0
Call Trace:
[c02dbef0] [c0007a84] show_stack+0x48/0x174 (unreliable)
[c02dbf20] [c006fa14] bad_page+0x9c/0x160
[c02dbf40] [c0071564] __free_pages_ok+0x2c8/0x3a8
[c02dbf70] [c02a9788] free_all_bootmem_core+0x1a4/0x240
[c02dbfa0] [c02a4544] mem_init+0x64/0x178
[c02dbfc0] [c029a67c] start_kernel+0x1d0/0x2a8
[c02dbff0] [c00003b4] skpinv+0x2dc/0x318
BUG: Bad page state in process swapper  pfn:2fce8
--------------------------------------------------------------------------------------

Can anyone kindly help me in understanding the issue.

with regards,

Deepak Gaur

^ permalink raw reply

* Re: Next April 28: boot failure on PowerPC with SLQB
From: Stephen Rothwell @ 2009-04-30  3:21 UTC (permalink / raw)
  To: Nick Piggin
  Cc: Christoph Lameter, linux-kernel, linuxppc-dev, Pekka Enberg,
	linux-next
In-Reply-To: <49F87FAB.9050408@in.ibm.com>

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

Hi Nick,

On Wed, 29 Apr 2009 21:56:19 +0530 Sachin Sant <sachinp@in.ibm.com> wrote:
>
> Nick Piggin wrote:
> > Does this help?
> > ---
> With the patch the machine boots past the failure point, but panics
> immediately with the following trace...

That patch also changes the behaviour of my machine.  I now get:

calling  .init_trace_printk+0x0/0x34 @ 1
initcall .init_trace_printk+0x0/0x34 returned 0 after 0 usecs
Unable to handle kernel paging request for data at address 0x00000030
Faulting instruction address: 0xc00000000008ae7c
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=128 NUMA pSeries
Modules linked in:
NIP: c00000000008ae7c LR: c0000000002b0cb8 CTR: 0000000000000000
REGS: c0000000d6357910 TRAP: 0300   Not tainted  (2.6.30-rc3-autokern1)
MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 28000084  XER: 0000000f
DAR: 0000000000000030, DSISR: 0000000042000000
TASK = c0000000fe368000[1] 'swapper' THREAD: c0000000d6354000 CPU: 0
GPR00: 0000000000000000 c0000000d6357b90 c000000000b04410 0000000000000028 
GPR04: c0000000006d7bd0 c000000000b95400 0000000000000000 c0000000fe368980 
GPR08: 000000000000004c 0000000000000291 0000000000000002 c0000000012bfd5c 
GPR12: 0000000048000088 c000000000b53280 0000000000000000 0000000002100000 
GPR16: c0000000006c8f50 c0000000006c76c8 0000000000000000 00000000003d8800 
GPR20: 00000000028c6bb0 c0000000007c6bb0 00000000028c6e20 c0000000007c6e20 
GPR24: c0000000006c89b8 0000000000000028 0000000000000003 c000000000b95400 
GPR28: c0000000006d7bd0 0000000000000010 c000000000a68708 0000000000000010 
NIP [c00000000008ae7c] .lockdep_init_map+0x3c/0x57c
LR [c0000000002b0cb8] .__spin_lock_init+0x44/0x90
Call Trace:
[c0000000d6357b90] [c000000000a68708] klist_remove_waiters+0xe210/0x2e2e0 (unreliable)
[c0000000d6357c50] [c0000000002b0cb8] .__spin_lock_init+0x44/0x90
[c0000000d6357d00] [c00000000054552c] .timer_cpu_notify+0xec/0x2c4
[c0000000d6357db0] [c000000000540bc0] .notifier_call_chain+0x5c/0xcc
[c0000000d6357e50] [c000000000544ee0] .cpu_up+0xe8/0x1c4
[c0000000d6357f00] [c00000000078fb94] .kernel_init+0xf8/0x220
[c0000000d6357f90] [c0000000000262b8] .kernel_thread+0x54/0x70
Instruction dump:
fbc1fff0 fbe1fff8 fae1ffb8 fb01ffc0 f8010010 fb41ffd0 fb61ffd8 fb81ffe0 
f821ff41 ebc2b160 38000000 7c791b78 <f8030008> 7cbf2b78 7cdd3378 e93e8138 
---[ end trace 31fd0ba7d8756001 ]---
Kernel panic - not syncing: Attempted to kill init!
Call Trace:
[c0000000d6357570] [c00000000000fd58] .show_stack+0x70/0x184 (unreliable)
[c0000000d6357620] [c00000000005ed34] .panic+0x80/0x1b4
[c0000000d63576c0] [c00000000006337c] .do_exit+0x84/0x6f0
[c0000000d6357780] [c000000000024350] .die+0x24c/0x27c
[c0000000d6357820] [c00000000002c1f4] .bad_page_fault+0xb8/0xd4
[c0000000d63578a0] [c00000000000534c] handle_page_fault+0x3c/0x5c
--- Exception: 300 at .lockdep_init_map+0x3c/0x57c
    LR = .__spin_lock_init+0x44/0x90
[c0000000d6357b90] [c000000000a68708] klist_remove_waiters+0xe210/0x2e2e0 (unreliable)
[c0000000d6357c50] [c0000000002b0cb8] .__spin_lock_init+0x44/0x90
[c0000000d6357d00] [c00000000054552c] .timer_cpu_notify+0xec/0x2c4
[c0000000d6357db0] [c000000000540bc0] .notifier_call_chain+0x5c/0xcc
[c0000000d6357e50] [c000000000544ee0] .cpu_up+0xe8/0x1c4
[c0000000d6357f00] [c00000000078fb94] .kernel_init+0xf8/0x220
[c0000000d6357f90] [c0000000000262b8] .kernel_thread+0x54/0x70

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: [MTD] ofpart: Partitions at same address cannot have the same name
From: Benjamin Herrenschmidt @ 2009-04-30  3:19 UTC (permalink / raw)
  To: Ricardo Ribalda Delgado; +Cc: linuxppc-dev, David.Woodhouse, linuxppc-embedded
In-Reply-To: <1240387541-16594-1-git-send-email-ricardo.ribalda@uam.es>

On Wed, 2009-04-22 at 10:05 +0200, Ricardo Ribalda Delgado wrote:
> Sometimes, an special partition is included in the device tree including all the
> partitions. Like in:
> 
> partition@ff000000 {
> 	reg = < 0x000000 0x800000 >;
> 	label = "Root File System";
> };
> partition@ff800000 {
> 	reg = < 0x800000 0x1a0000 >;
> 	label = "Bitstream";
> };
> ...
> partitionAll@ff000000 {
> 	reg = < 0x000000 0x1000000 >;
> 	label = "Full FLASH";
> };
> 
> Because two nodes of a device tree cannot have the same name, but all the 
> partitions must be named "partition", this special partition is invalid.
> 
> This patch makes ofpart.c only check for the firt part of the name, and 
> ignore the rest, allowing this special partition.

I fail to see the point of this "special" partition in the first
place...

Things would make more sense if you had a full flash device
whose child nodes are the partitions.

Ben.

^ permalink raw reply

* Re: [v2 PATCH 1/3] EDAC: Add CPC925 Memory Controller driver
From: Benjamin Herrenschmidt @ 2009-04-30  3:08 UTC (permalink / raw)
  To: Harry Ciao; +Cc: norsk5, bluesmoke-devel, linux-kernel, linuxppc-dev
In-Reply-To: <1239602716-18352-2-git-send-email-qingtao.cao@windriver.com>

On Mon, 2009-04-13 at 14:05 +0800, Harry Ciao wrote:
> Introduce IBM CPC925 EDAC driver, which makes use of ECC, CPU and
> HyperTransport Link error detections and corrections on the IBM
> CPC925 Bridge and Memory Controller.

Ah, fun ! I'm pretty sure I wrote a driver for that chipset a while
ago ... looks like I forgot to merge it :-)

I'll review in more details asap

Thanks,
Ben.

> Signed-off-by: Harry Ciao <qingtao.cao@windriver.com>
> ---
>  drivers/edac/Kconfig       |    9 +
>  drivers/edac/Makefile      |    1 +
>  drivers/edac/cpc925_edac.c | 1020 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1030 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/edac/cpc925_edac.c
> 
> diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
> index 13ccec6..1a320b4 100644
> --- a/drivers/edac/Kconfig
> +++ b/drivers/edac/Kconfig
> @@ -188,4 +188,13 @@ config EDAC_AMD8131
>  	  Support for error detection and correction on the
>  	  AMD8131 HyperTransport PCI-X Tunnel chip.
>  
> +config EDAC_CPC925
> +	tristate "IBM CPC925 Memory Controller (PPC970FX)"
> +	depends on EDAC_MM_EDAC && PPC64
> +	help
> +	  Support for error detection and correction on the
> +	  IBM CPC925 Bridge and Memory Controller, which is
> +	  a companion chip to the PowerPC 970 family of
> +	  processors.
> +
>  endif # EDAC
> diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
> index 407601f..b4e0d86 100644
> --- a/drivers/edac/Makefile
> +++ b/drivers/edac/Makefile
> @@ -36,4 +36,5 @@ obj-$(CONFIG_EDAC_MV64X60)		+= mv64x60_edac.o
>  obj-$(CONFIG_EDAC_CELL)			+= cell_edac.o
>  obj-$(CONFIG_EDAC_AMD8111)		+= amd8111_edac.o
>  obj-$(CONFIG_EDAC_AMD8131)		+= amd8131_edac.o
> +obj-$(CONFIG_EDAC_CPC925)		+= cpc925_edac.o
>  
> diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
> new file mode 100644
> index 0000000..7236847
> --- /dev/null
> +++ b/drivers/edac/cpc925_edac.c
> @@ -0,0 +1,1020 @@
> +/*
> + * cpc925_edac.c, EDAC driver for IBM CPC925 Bridge and Memory Controller.
> + *
> + * Copyright (c) 2008 Wind River Systems, Inc.
> + *
> + * Authors:	Cao Qingtao <qingtao.cao@windriver.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/edac.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#include "edac_core.h"
> +#include "edac_module.h"
> +
> +#define CPC925_EDAC_REVISION	" Ver: 1.0.0 " __DATE__
> +#define CPC925_EDAC_MOD_STR	"cpc925_edac"
> +
> +#define cpc925_printk(level, fmt, arg...) \
> +	edac_printk(level, "CPC925", fmt, ##arg)
> +
> +#define cpc925_mc_printk(mci, level, fmt, arg...) \
> +	edac_mc_chipset_printk(mci, level, "CPC925", fmt, ##arg)
> +
> +/*
> + * CPC925 registers are of 32 bits with bit0 defined at the
> + * most significant bit and bit31 at that of least significant.
> + */
> +#define CPC925_BITS_PER_REG	32
> +#define CPC925_BIT(nr)		(1UL << (CPC925_BITS_PER_REG - 1 - nr))
> +
> +/*
> + * EDAC device names for the error detections of
> + * CPU Interface and Hypertransport Link.
> + */
> +#define CPC925_CPU_ERR_DEV	"cpu"
> +#define CPC925_HT_LINK_DEV	"htlink"
> +
> +/* Suppose DDR Refresh cycle is 15.6 microsecond */
> +#define CPC925_REF_FREQ		0xFA69
> +#define CPC925_SCRUB_BLOCK_SIZE 64	/* bytes */
> +#define CPC925_NR_CSROWS	8
> +
> +/*
> + * All registers and bits definitions are taken from
> + * "CPC925 Bridge and Memory Controller User Manual, SA14-2761-02".
> + */
> +
> +/*
> + * CPU and Memory Controller Registers
> + */
> +/************************************************************
> + *	Processor Interface Exception Mask Register (APIMASK)
> + ************************************************************/
> +#define REG_APIMASK_OFFSET	0x30070
> +enum apimask_bits {
> +	APIMASK_DART	= CPC925_BIT(0), /* DART Exception */
> +	APIMASK_ADI0	= CPC925_BIT(1), /* Handshake Error on PI0_ADI */
> +	APIMASK_ADI1	= CPC925_BIT(2), /* Handshake Error on PI1_ADI */
> +	APIMASK_STAT	= CPC925_BIT(3), /* Status Exception */
> +	APIMASK_DERR	= CPC925_BIT(4), /* Data Error Exception */
> +	APIMASK_ADRS0	= CPC925_BIT(5), /* Addressing Exception on PI0 */
> +	APIMASK_ADRS1	= CPC925_BIT(6), /* Addressing Exception on PI1 */
> +					 /* BIT(7) Reserved */
> +	APIMASK_ECC_UE_H = CPC925_BIT(8), /* UECC upper */
> +	APIMASK_ECC_CE_H = CPC925_BIT(9), /* CECC upper */
> +	APIMASK_ECC_UE_L = CPC925_BIT(10), /* UECC lower */
> +	APIMASK_ECC_CE_L = CPC925_BIT(11), /* CECC lower */
> +
> +	CPU_MASK_ENABLE = (APIMASK_DART | APIMASK_ADI0 | APIMASK_ADI1 |
> +			   APIMASK_STAT | APIMASK_DERR | APIMASK_ADRS0 |
> +			   APIMASK_ADRS1),
> +	ECC_MASK_ENABLE = (APIMASK_ECC_UE_H | APIMASK_ECC_CE_H |
> +			   APIMASK_ECC_UE_L | APIMASK_ECC_CE_L),
> +};
> +
> +/************************************************************
> + *	Processor Interface Exception Register (APIEXCP)
> + ************************************************************/
> +#define REG_APIEXCP_OFFSET	0x30060
> +enum apiexcp_bits {
> +	APIEXCP_DART	= CPC925_BIT(0), /* DART Exception */
> +	APIEXCP_ADI0	= CPC925_BIT(1), /* Handshake Error on PI0_ADI */
> +	APIEXCP_ADI1	= CPC925_BIT(2), /* Handshake Error on PI1_ADI */
> +	APIEXCP_STAT	= CPC925_BIT(3), /* Status Exception */
> +	APIEXCP_DERR	= CPC925_BIT(4), /* Data Error Exception */
> +	APIEXCP_ADRS0	= CPC925_BIT(5), /* Addressing Exception on PI0 */
> +	APIEXCP_ADRS1	= CPC925_BIT(6), /* Addressing Exception on PI1 */
> +					 /* BIT(7) Reserved */
> +	APIEXCP_ECC_UE_H = CPC925_BIT(8), /* UECC upper */
> +	APIEXCP_ECC_CE_H = CPC925_BIT(9), /* CECC upper */
> +	APIEXCP_ECC_UE_L = CPC925_BIT(10), /* UECC lower */
> +	APIEXCP_ECC_CE_L = CPC925_BIT(11), /* CECC lower */
> +
> +	CPU_EXCP_DETECTED = (APIEXCP_DART | APIEXCP_ADI0 | APIEXCP_ADI1 |
> +			     APIEXCP_STAT | APIEXCP_DERR | APIEXCP_ADRS0 |
> +			     APIEXCP_ADRS1),
> +	UECC_EXCP_DETECTED = (APIEXCP_ECC_UE_H | APIEXCP_ECC_UE_L),
> +	CECC_EXCP_DETECTED = (APIEXCP_ECC_CE_H | APIEXCP_ECC_CE_L),
> +	ECC_EXCP_DETECTED = (UECC_EXCP_DETECTED | CECC_EXCP_DETECTED),
> +};
> +
> +/************************************************************
> + *	Memory Bus Configuration Register (MBCR)
> +************************************************************/
> +#define REG_MBCR_OFFSET		0x2190
> +#define MBCR_64BITCFG_SHIFT	23
> +#define MBCR_64BITCFG_MASK	(1UL << MBCR_64BITCFG_SHIFT)
> +#define MBCR_64BITBUS_SHIFT	22
> +#define MBCR_64BITBUS_MASK	(1UL << MBCR_64BITBUS_SHIFT)
> +
> +/************************************************************
> + *	Memory Bank Mode Register (MBMR)
> +************************************************************/
> +#define REG_MBMR_OFFSET		0x21C0
> +#define MBMR_MODE_MAX_VALUE	0xF
> +#define MBMR_MODE_SHIFT		25
> +#define MBMR_MODE_MASK		(MBMR_MODE_MAX_VALUE << MBMR_MODE_SHIFT)
> +#define MBMR_BBA_SHIFT		24
> +#define MBMR_BBA_MASK		(1UL << MBMR_BBA_SHIFT)
> +
> +/************************************************************
> + *	Memory Bank Boundary Address Register (MBBAR)
> + ************************************************************/
> +#define REG_MBBAR_OFFSET	0x21D0
> +#define MBBAR_BBA_MAX_VALUE	0xFF
> +#define MBBAR_BBA_SHIFT		24
> +#define MBBAR_BBA_MASK		(MBBAR_BBA_MAX_VALUE << MBBAR_BBA_SHIFT)
> +
> +/************************************************************
> + *	Memory Scrub Control Register (MSCR)
> + ************************************************************/
> +#define REG_MSCR_OFFSET		0x2400
> +#define MSCR_SCRUB_MOD_MASK	0xC0000000 /* scrub_mod - bit0:1*/
> +#define MSCR_BACKGR_SCRUB	0x40000000 /* 01 */
> +#define MSCR_SI_SHIFT		16 	/* si - bit8:15*/
> +#define MSCR_SI_MAX_VALUE	0xFF
> +#define MSCR_SI_MASK		(MSCR_SI_MAX_VALUE << MSCR_SI_SHIFT)
> +
> +/************************************************************
> + *	Memory Scrub Range Start Register (MSRSR)
> + ************************************************************/
> +#define REG_MSRSR_OFFSET	0x2410
> +
> +/************************************************************
> + *	Memory Scrub Range End Register (MSRER)
> + ************************************************************/
> +#define REG_MSRER_OFFSET	0x2420
> +
> +/************************************************************
> + *	Memory Scrub Pattern Register (MSPR)
> + ************************************************************/
> +#define REG_MSPR_OFFSET		0x2430
> +
> +/************************************************************
> + *	Memory Check Control Register (MCCR)
> + ************************************************************/
> +#define REG_MCCR_OFFSET		0x2440
> +enum mccr_bits {
> +	MCCR_ECC_EN	= CPC925_BIT(0), /* ECC high and low check */
> +};
> +
> +/************************************************************
> + *	Memory Check Range End Register (MCRER)
> + ************************************************************/
> +#define REG_MCRER_OFFSET	0x2450
> +
> +/************************************************************
> + *	Memory Error Address Register (MEAR)
> + ************************************************************/
> +#define REG_MEAR_OFFSET		0x2460
> +#define MEAR_BCNT_MAX_VALUE	0x3
> +#define MEAR_BCNT_SHIFT		30
> +#define MEAR_BCNT_MASK		(MEAR_BCNT_MAX_VALUE << MEAR_BCNT_SHIFT)
> +#define MEAR_RANK_MAX_VALUE	0x7
> +#define MEAR_RANK_SHIFT		27
> +#define MEAR_RANK_MASK		(MEAR_RANK_MAX_VALUE << MEAR_RANK_SHIFT)
> +#define MEAR_COL_MAX_VALUE	0x7FF
> +#define MEAR_COL_SHIFT		16
> +#define MEAR_COL_MASK		(MEAR_COL_MAX_VALUE << MEAR_COL_SHIFT)
> +#define MEAR_BANK_MAX_VALUE	0x3
> +#define MEAR_BANK_SHIFT		14
> +#define MEAR_BANK_MASK		(MEAR_BANK_MAX_VALUE << MEAR_BANK_SHIFT)
> +#define MEAR_ROW_MASK		0x00003FFF
> +
> +/************************************************************
> + *	Memory Error Syndrome Register (MESR)
> + ************************************************************/
> +#define REG_MESR_OFFSET		0x2470
> +#define MESR_ECC_SYN_H_MASK	0xFF00
> +#define MESR_ECC_SYN_L_MASK	0x00FF
> +
> +/************************************************************
> + *	Memory Mode Control Register (MMCR)
> + ************************************************************/
> +#define REG_MMCR_OFFSET		0x2500
> +enum mmcr_bits {
> +	MMCR_REG_DIMM_MODE = CPC925_BIT(3),
> +};
> +
> +/*
> + * HyperTransport Link Registers
> + */
> +/************************************************************
> + *  Error Handling/Enumeration Scratch Pad Register (ERRCTRL)
> + ************************************************************/
> +#define REG_ERRCTRL_OFFSET	0x70140
> +enum errctrl_bits {			 /* nonfatal interrupts for */
> +	ERRCTRL_SERR_NF	= CPC925_BIT(0), /* system error */
> +	ERRCTRL_CRC_NF	= CPC925_BIT(1), /* CRC error */
> +	ERRCTRL_RSP_NF	= CPC925_BIT(2), /* Response error */
> +	ERRCTRL_EOC_NF	= CPC925_BIT(3), /* End-Of-Chain error */
> +	ERRCTRL_OVF_NF	= CPC925_BIT(4), /* Overflow error */
> +	ERRCTRL_PROT_NF	= CPC925_BIT(5), /* Protocol error */
> +
> +	ERRCTRL_RSP_ERR	= CPC925_BIT(6), /* Response error received */
> +	ERRCTRL_CHN_FAL = CPC925_BIT(7), /* Sync flooding detected */
> +
> +	HT_ERRCTRL_ENABLE = (ERRCTRL_SERR_NF | ERRCTRL_CRC_NF |
> +			     ERRCTRL_RSP_NF | ERRCTRL_EOC_NF |
> +			     ERRCTRL_OVF_NF | ERRCTRL_PROT_NF),
> +	HT_ERRCTRL_DETECTED = (ERRCTRL_RSP_ERR | ERRCTRL_CHN_FAL),
> +};
> +
> +/************************************************************
> + *  Link Configuration and Link Control Register (LINKCTRL)
> + ************************************************************/
> +#define REG_LINKCTRL_OFFSET	0x70110
> +enum linkctrl_bits {
> +	LINKCTRL_CRC_ERR	= (CPC925_BIT(22) | CPC925_BIT(23)),
> +	LINKCTRL_LINK_FAIL	= CPC925_BIT(27),
> +
> +	HT_LINKCTRL_DETECTED	= (LINKCTRL_CRC_ERR | LINKCTRL_LINK_FAIL),
> +};
> +
> +/************************************************************
> + *  Link FreqCap/Error/Freq/Revision ID Register (LINKERR)
> + ************************************************************/
> +#define REG_LINKERR_OFFSET	0x70120
> +enum linkerr_bits {
> +	LINKERR_EOC_ERR		= CPC925_BIT(17), /* End-Of-Chain error */
> +	LINKERR_OVF_ERR		= CPC925_BIT(18), /* Receive Buffer Overflow */
> +	LINKERR_PROT_ERR	= CPC925_BIT(19), /* Protocol error */
> +
> +	HT_LINKERR_DETECTED	= (LINKERR_EOC_ERR | LINKERR_OVF_ERR |
> +				   LINKERR_PROT_ERR),
> +};
> +
> +/************************************************************
> + *	Bridge Control Register (BRGCTRL)
> + ************************************************************/
> +#define REG_BRGCTRL_OFFSET	0x70300
> +enum brgctrl_bits {
> +	BRGCTRL_DETSERR = CPC925_BIT(0), /* SERR on Secondary Bus */
> +	BRGCTRL_SECBUSRESET = CPC925_BIT(9), /* Secondary Bus Reset */
> +};
> +
> +/* Private structure for edac memory controller */
> +struct cpc925_mc_pdata {
> +	void __iomem *vbase;
> +	unsigned long total_mem;
> +	const char *name;
> +	int edac_idx;
> +};
> +
> +/* Private structure for common edac device */
> +struct cpc925_dev_info {
> +	void __iomem *vbase;
> +	struct platform_device *pdev;
> +	char *ctl_name;
> +	int edac_idx;
> +	struct edac_device_ctl_info *edac_dev;
> +	void (*init)(struct cpc925_dev_info *dev_info);
> +	void (*exit)(struct cpc925_dev_info *dev_info);
> +	void (*check)(struct edac_device_ctl_info *edac_dev);
> +};
> +
> +
> +static int edac_mc_idx;
> +
> +/* Get total memory size from Open Firmware DTB */
> +static void get_total_mem(struct cpc925_mc_pdata *pdata)
> +{
> +	struct device_node *np = NULL;
> +	const unsigned int *reg, *reg_end;
> +	int len, sw, aw;
> +	unsigned long start, size;
> +
> +	np = of_find_node_by_type(NULL, "memory");
> +	if (!np)
> +		return;
> +
> +	aw = of_n_addr_cells(np);
> +	sw = of_n_size_cells(np);
> +	reg = (const unsigned int *)of_get_property(np, "reg", &len);
> +	reg_end = reg + len/4;
> +
> +	pdata->total_mem = 0;
> +	do {
> +		start = of_read_number(reg, aw);
> +		reg += aw;
> +		size = of_read_number(reg, sw);
> +		reg += sw;
> +		debugf1("%s: start 0x%lx, size 0x%lx\n", __func__,
> +			start, size);
> +		pdata->total_mem += size;
> +	} while (reg < reg_end);
> +
> +	of_node_put(np);
> +	debugf0("%s: total_mem 0x%lx\n", __func__, pdata->total_mem);
> +}
> +
> +static void cpc925_init_csrows(struct mem_ctl_info *mci)
> +{
> +	struct cpc925_mc_pdata *pdata = mci->pvt_info;
> +	struct csrow_info *csrow;
> +	int index;
> +	u32 mbmr, mbbar, bba;
> +	unsigned long row_size, last_nr_pages = 0;
> +
> +	get_total_mem(pdata);
> +
> +	for (index = 0; index < mci->nr_csrows; index++) {
> +		mbmr = __raw_readl(pdata->vbase + REG_MBMR_OFFSET +
> +				   0x20 * index);
> +		mbbar = __raw_readl(pdata->vbase + REG_MBBAR_OFFSET +
> +				   0x20 + index);
> +		bba = (((mbmr & MBMR_BBA_MASK) >> MBMR_BBA_SHIFT) << 8) |
> +		       ((mbbar & MBBAR_BBA_MASK) >> MBBAR_BBA_SHIFT);
> +
> +		if (bba == 0)
> +			continue; /* not populated */
> +
> +		csrow = &mci->csrows[index];
> +
> +		row_size = bba * (1UL << 28);	/* 256M */
> +		csrow->first_page = last_nr_pages;
> +		csrow->nr_pages = row_size >> PAGE_SHIFT;
> +		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
> +		last_nr_pages = csrow->last_page + 1;
> +
> +		csrow->mtype = MEM_RDDR;
> +		csrow->edac_mode = EDAC_SECDED;
> +
> +		switch (csrow->nr_channels) {
> +		case 1: /* Single channel */
> +			csrow->grain = 32; /* four-beat burst of 32 bytes */
> +			break;
> +		case 2: /* Dual channel */
> +		default:
> +			csrow->grain = 64; /* four-beat burst of 64 bytes */
> +			break;
> +		}
> +
> +		switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
> +		case 6: /* 0110, no way to differentiate X8 VS X16 */
> +		case 5:	/* 0101 */
> +		case 8: /* 1000 */
> +			csrow->dtype = DEV_X16;
> +			break;
> +		case 7: /* 0111 */
> +		case 9: /* 1001 */
> +			csrow->dtype = DEV_X8;
> +			break;
> +		default:
> +			csrow->dtype = DEV_UNKNOWN;
> +			break;
> +		}
> +	}
> +}
> +
> +/* Enable memory controller ECC detection */
> +static void cpc925_mc_init(struct mem_ctl_info *mci)
> +{
> +	struct cpc925_mc_pdata *pdata = mci->pvt_info;
> +	u32 apimask;
> +	u32 mccr;
> +
> +	/* Enable various ECC error exceptions */
> +	apimask = __raw_readl(pdata->vbase + REG_APIMASK_OFFSET);
> +	if ((apimask & ECC_MASK_ENABLE) == 0) {
> +		apimask |= ECC_MASK_ENABLE;
> +		__raw_writel(apimask, pdata->vbase + REG_APIMASK_OFFSET);
> +	}
> +
> +	/* Enable ECC detection */
> +	mccr = __raw_readl(pdata->vbase + REG_MCCR_OFFSET);
> +	if ((mccr & MCCR_ECC_EN) == 0) {
> +		mccr |= MCCR_ECC_EN;
> +		__raw_writel(mccr, pdata->vbase + REG_MCCR_OFFSET);
> +	}
> +}
> +
> +/* Disable memory controller ECC detection */
> +static void cpc925_mc_exit(struct mem_ctl_info *mci)
> +{
> +	/*
> +	 * WARNING:
> +	 * We are supposed to clear the ECC error detection bits,
> +	 * and it will be no problem to do so. However, once they
> +	 * are cleared here if we want to re-install CPC925 EDAC
> +	 * module later, setting them up in cpc925_mc_init() will
> +	 * trigger machine check exception.
> +	 * Also, it's ok to leave ECC error detection bits enabled,
> +	 * since they are reset to 1 by default or by boot loader.
> +	 */
> +
> +	return;
> +}
> +
> +/*
> + * Revert DDR column/row/bank addresses into page frame number and
> + * offset in page.
> + *
> + * Suppose memory mode is 0x0111(128-bit mode, identical DIMM pairs),
> + * physical address(PA) bits to column address(CA) bits mappings are:
> + * CA	0   1   2   3   4   5   6   7   8   9   10
> + * PA	59  58  57  56  55  54  53  52  51  50  49
> + *
> + * physical address(PA) bits to bank address(BA) bits mappings are:
> + * BA	0   1
> + * PA	43  44
> + *
> + * physical address(PA) bits to row address(RA) bits mappings are:
> + * RA	0   1   2   3   4   5   6   7   8   9   10   11   12
> + * PA	36  35  34  48  47  46  45  40  41  42  39   38   37
> + */
> +static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
> +		unsigned long *pfn, unsigned long *offset, int *csrow)
> +{
> +	u32 bcnt, rank, col, bank, row;
> +	u32 c;
> +	unsigned long pa;
> +	int i;
> +
> +	bcnt = (mear & MEAR_BCNT_MASK) >> MEAR_BCNT_SHIFT;
> +	rank = (mear & MEAR_RANK_MASK) >> MEAR_RANK_SHIFT;
> +	col = (mear & MEAR_COL_MASK) >> MEAR_COL_SHIFT;
> +	bank = (mear & MEAR_BANK_MASK) >> MEAR_BANK_SHIFT;
> +	row = mear & MEAR_ROW_MASK;
> +
> +	*csrow = rank;
> +
> +#ifdef CONFIG_EDAC_DEBUG
> +	if (mci->csrows[rank].first_page == 0) {
> +		cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a "
> +			"non-populated csrow, broken hardware?\n");
> +		return;
> +	}
> +#endif
> +
> +	/* Revert csrow number */
> +	pa = mci->csrows[rank].first_page << PAGE_SHIFT;
> +
> +	/* Revert column address */
> +	col += bcnt;
> +	for (i = 0; i < 11; i++) {
> +		c = col & 0x1;
> +		col >>= 1;
> +		pa |= c << (14 - i);
> +	}
> +
> +	/* Revert bank address */
> +	pa |= bank << 19;
> +
> +	/* Revert row address, in 4 steps */
> +	for (i = 0; i < 3; i++) {
> +		c = row & 0x1;
> +		row >>= 1;
> +		pa |= c << (26 - i);
> +	}
> +
> +	for (i = 0; i < 3; i++) {
> +		c = row & 0x1;
> +		row >>= 1;
> +		pa |= c << (21 + i);
> +	}
> +
> +	for (i = 0; i < 4; i++) {
> +		c = row & 0x1;
> +		row >>= 1;
> +		pa |= c << (18 - i);
> +	}
> +
> +	for (i = 0; i < 3; i++) {
> +		c = row & 0x1;
> +		row >>= 1;
> +		pa |= c << (29 - i);
> +	}
> +
> +	*offset = pa & (PAGE_SIZE - 1);
> +	*pfn = pa >> PAGE_SHIFT;
> +
> +	debugf0("%s: ECC physical address 0x%lx\n", __func__, pa);
> +}
> +
> +static int cpc925_mc_find_channel(struct mem_ctl_info *mci, u16 syndrome)
> +{
> +	if ((syndrome & MESR_ECC_SYN_H_MASK) == 0)
> +		return 0;
> +
> +	if ((syndrome & MESR_ECC_SYN_L_MASK) == 0)
> +		return 1;
> +
> +	cpc925_mc_printk(mci, KERN_INFO, "Unexpected syndrome value: 0x%x\n",
> +			 syndrome);
> +	return 1;
> +}
> +
> +/* Check memory controller registers for ECC errors */
> +static void cpc925_mc_check(struct mem_ctl_info *mci)
> +{
> +	struct cpc925_mc_pdata *pdata = mci->pvt_info;
> +	u32 apiexcp;
> +	u32 mear;
> +	u32 mesr;
> +	u16 syndrome;
> +	unsigned long pfn = 0, offset = 0;
> +	int csrow = 0, channel = 0;
> +
> +	/* APIEXCP is cleared when read */
> +	apiexcp = __raw_readl(pdata->vbase + REG_APIEXCP_OFFSET);
> +	if ((apiexcp & ECC_EXCP_DETECTED) == 0)
> +		return;
> +
> +	mesr = __raw_readl(pdata->vbase + REG_MESR_OFFSET);
> +	syndrome = mesr | (MESR_ECC_SYN_H_MASK | MESR_ECC_SYN_L_MASK);
> +
> +	mear = __raw_readl(pdata->vbase + REG_MEAR_OFFSET);
> +
> +	/* Revert column/row addresses into page frame number, etc */
> +	cpc925_mc_get_pfn(mci, mear, &pfn, &offset, &csrow);
> +
> +	if (apiexcp & UECC_EXCP_DETECTED) {
> +		cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n");
> +		channel = cpc925_mc_find_channel(mci, syndrome);
> +		edac_mc_handle_ce(mci, pfn, offset, syndrome,
> +				  csrow, channel, mci->ctl_name);
> +	}
> +
> +	if (apiexcp & CECC_EXCP_DETECTED) {
> +		cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n");
> +		edac_mc_handle_ue(mci, pfn, offset,
> +				  csrow, mci->ctl_name);
> +	}
> +
> +	cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n");
> +	cpc925_mc_printk(mci, KERN_INFO, "APIMASK		0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_APIMASK_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "APIEXCP		0x%08x\n",
> +		apiexcp);
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Ctrl	0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MSCR_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Rge Start	0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MSRSR_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Rge End	0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MSRER_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Pattern	0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MSPR_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Chk Ctrl		0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MCCR_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Chk Rge End	0x%08x\n",
> +		__raw_readl(pdata->vbase + REG_MCRER_OFFSET));
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Err Address	0x%08x\n",
> +		mesr);
> +	cpc925_mc_printk(mci, KERN_INFO, "Mem Err Syndrome	0x%08x\n",
> +		syndrome);
> +}
> +
> +/******************** CPU err device********************************/
> +/* Enable CPU Errors detection */
> +static void cpc925_cpu_init(struct cpc925_dev_info *dev_info)
> +{
> +	u32 apimask;
> +
> +	apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET);
> +	if ((apimask & CPU_MASK_ENABLE) == 0) {
> +		apimask |= CPU_MASK_ENABLE;
> +		__raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET);
> +	}
> +}
> +
> +/* Disable CPU Errors detection */
> +static void cpc925_cpu_exit(struct cpc925_dev_info *dev_info)
> +{
> +	/*
> +	 * WARNING:
> +	 * We are supposed to clear the CPU error detection bits,
> +	 * and it will be no problem to do so. However, once they
> +	 * are cleared here if we want to re-install CPC925 EDAC
> +	 * module later, setting them up in cpc925_cpu_init() will
> +	 * trigger machine check exception.
> +	 * Also, it's ok to leave CPU error detection bits enabled,
> +	 * since they are reset to 1 by default.
> +	 */
> +
> +	return;
> +}
> +
> +/* Check for CPU Errors */
> +static void cpc925_cpu_check(struct edac_device_ctl_info *edac_dev)
> +{
> +	struct cpc925_dev_info *dev_info = edac_dev->pvt_info;
> +	u32 apiexcp;
> +	u32 apimask;
> +
> +	/* APIEXCP is cleared when read */
> +	apiexcp = __raw_readl(dev_info->vbase + REG_APIEXCP_OFFSET);
> +	if ((apiexcp & CPU_EXCP_DETECTED) == 0)
> +		return;
> +
> +	apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET);
> +	cpc925_printk(KERN_INFO, "Processor Interface Fault\n"
> +				 "Processor Interface register dump:\n");
> +	cpc925_printk(KERN_INFO, "APIMASK		0x%08x\n", apimask);
> +	cpc925_printk(KERN_INFO, "APIEXCP		0x%08x\n", apiexcp);
> +
> +	edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
> +}
> +
> +/******************** HT Link err device****************************/
> +/* Enable HyperTransport Link Error detection */
> +static void cpc925_htlink_init(struct cpc925_dev_info *dev_info)
> +{
> +	u32 ht_errctrl;
> +
> +	ht_errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET);
> +	if ((ht_errctrl & HT_ERRCTRL_ENABLE) == 0) {
> +		ht_errctrl |= HT_ERRCTRL_ENABLE;
> +		__raw_writel(ht_errctrl, dev_info->vbase + REG_ERRCTRL_OFFSET);
> +	}
> +}
> +
> +/* Disable HyperTransport Link Error detection */
> +static void cpc925_htlink_exit(struct cpc925_dev_info *dev_info)
> +{
> +	u32 ht_errctrl;
> +
> +	ht_errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET);
> +	ht_errctrl &= ~HT_ERRCTRL_ENABLE;
> +	__raw_writel(ht_errctrl, dev_info->vbase + REG_ERRCTRL_OFFSET);
> +}
> +
> +/* Check for HyperTransport Link errors */
> +static void cpc925_htlink_check(struct edac_device_ctl_info *edac_dev)
> +{
> +	struct cpc925_dev_info *dev_info = edac_dev->pvt_info;
> +	u32 brgctrl = __raw_readl(dev_info->vbase + REG_BRGCTRL_OFFSET);
> +	u32 linkctrl = __raw_readl(dev_info->vbase + REG_LINKCTRL_OFFSET);
> +	u32 errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET);
> +	u32 linkerr = __raw_readl(dev_info->vbase + REG_LINKERR_OFFSET);
> +
> +	if (!((brgctrl & BRGCTRL_DETSERR) ||
> +	      (linkctrl & HT_LINKCTRL_DETECTED) ||
> +	      (errctrl & HT_ERRCTRL_DETECTED) ||
> +	      (linkerr & HT_LINKERR_DETECTED)))
> +		return;
> +
> +	cpc925_printk(KERN_INFO, "HT Link Fault\n"
> +				 "HT register dump:\n");
> +	cpc925_printk(KERN_INFO, "Bridge Ctrl			0x%08x\n",
> +		      brgctrl);
> +	cpc925_printk(KERN_INFO, "Link Config Ctrl		0x%08x\n",
> +		      linkctrl);
> +	cpc925_printk(KERN_INFO, "Error Enum and Ctrl		0x%08x\n",
> +		      errctrl);
> +	cpc925_printk(KERN_INFO, "Link Error			0x%08x\n",
> +		      linkerr);
> +
> +	/* Clear by write 1 */
> +	if (brgctrl & BRGCTRL_DETSERR)
> +		__raw_writel(BRGCTRL_DETSERR,
> +				dev_info->vbase + REG_BRGCTRL_OFFSET);
> +
> +	if (linkctrl & HT_LINKCTRL_DETECTED)
> +		__raw_writel(HT_LINKCTRL_DETECTED,
> +				dev_info->vbase + REG_LINKCTRL_OFFSET);
> +
> +	/* Initiate Secondary Bus Reset to clear the chain failure */
> +	if (errctrl & ERRCTRL_CHN_FAL)
> +		__raw_writel(BRGCTRL_SECBUSRESET,
> +				dev_info->vbase + REG_BRGCTRL_OFFSET);
> +
> +	if (errctrl & ERRCTRL_RSP_ERR)
> +		__raw_writel(ERRCTRL_RSP_ERR,
> +				dev_info->vbase + REG_ERRCTRL_OFFSET);
> +
> +	if (linkerr & HT_LINKERR_DETECTED)
> +		__raw_writel(HT_LINKERR_DETECTED,
> +				dev_info->vbase + REG_LINKERR_OFFSET);
> +
> +	edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
> +}
> +
> +static struct cpc925_dev_info cpc925_devs[] = {
> +	{
> +	.ctl_name = CPC925_CPU_ERR_DEV,
> +	.init = cpc925_cpu_init,
> +	.exit = cpc925_cpu_exit,
> +	.check = cpc925_cpu_check,
> +	},
> +	{
> +	.ctl_name = CPC925_HT_LINK_DEV,
> +	.init = cpc925_htlink_init,
> +	.exit = cpc925_htlink_exit,
> +	.check = cpc925_htlink_check,
> +	},
> +	{0}, /* Terminated by NULL */
> +};
> +
> +/*
> + * Add CPU Err detection and HyperTransport Link Err detection
> + * as common "edac_device", they have no corresponding device
> + * nodes in the Open Firmware DTB and we have to add platform
> + * devices for them. Also, they will share the MMIO with that
> + * of memory controller.
> + */
> +static void cpc925_add_edac_devices(void __iomem *vbase)
> +{
> +	struct cpc925_dev_info *dev_info;
> +
> +	if (!vbase) {
> +		cpc925_printk(KERN_ERR, "MMIO not established yet\n");
> +		return;
> +	}
> +
> +	for (dev_info = &cpc925_devs[0]; dev_info->init; dev_info++) {
> +		dev_info->vbase = vbase;
> +		dev_info->pdev = platform_device_register_simple(
> +					dev_info->ctl_name, 0, NULL, 0);
> +		if (IS_ERR(dev_info->pdev)) {
> +			cpc925_printk(KERN_ERR,
> +				"Can't register platform device for %s\n",
> +				dev_info->ctl_name);
> +			continue;
> +		}
> +
> +		/*
> +		 * Don't have to allocate private structure but
> +		 * make use of cpc925_devs[] instead.
> +		 */
> +		dev_info->edac_idx = edac_device_alloc_index();
> +		dev_info->edac_dev =
> +			edac_device_alloc_ctl_info(0, dev_info->ctl_name,
> +				1, NULL, 0, 0, NULL, 0, dev_info->edac_idx);
> +		if (!dev_info->edac_dev) {
> +			cpc925_printk(KERN_ERR, "No memory for edac device\n");
> +			goto err1;
> +		}
> +
> +		dev_info->edac_dev->pvt_info = dev_info;
> +		dev_info->edac_dev->dev = &dev_info->pdev->dev;
> +		dev_info->edac_dev->ctl_name = dev_info->ctl_name;
> +		dev_info->edac_dev->mod_name = CPC925_EDAC_MOD_STR;
> +		dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
> +
> +		if (edac_op_state == EDAC_OPSTATE_POLL)
> +			dev_info->edac_dev->edac_check = dev_info->check;
> +
> +		if (dev_info->init)
> +			dev_info->init(dev_info);
> +
> +		if (edac_device_add_device(dev_info->edac_dev) > 0) {
> +			cpc925_printk(KERN_ERR,
> +				"Unable to add edac device for %s\n",
> +				dev_info->ctl_name);
> +			goto err2;
> +		}
> +
> +		debugf0("%s: Successfully added edac device for %s\n",
> +			__func__, dev_info->ctl_name);
> +
> +		continue;
> +
> +err2:
> +		if (dev_info->exit)
> +			dev_info->exit(dev_info);
> +		edac_device_free_ctl_info(dev_info->edac_dev);
> +err1:
> +		platform_device_unregister(dev_info->pdev);
> +	}
> +}
> +
> +/*
> + * Delete the common "edac_device" for CPU Err Detection
> + * and HyperTransport Link Err Detection
> + */
> +static void cpc925_del_edac_devices(void)
> +{
> +	struct cpc925_dev_info *dev_info;
> +
> +	for (dev_info = &cpc925_devs[0]; dev_info->init; dev_info++) {
> +		if (dev_info->edac_dev) {
> +			edac_device_del_device(dev_info->edac_dev->dev);
> +			edac_device_free_ctl_info(dev_info->edac_dev);
> +			platform_device_unregister(dev_info->pdev);
> +		}
> +
> +		if (dev_info->exit)
> +			dev_info->exit(dev_info);
> +
> +		debugf0("%s: Successfully deleted edac device for %s\n",
> +			__func__, dev_info->ctl_name);
> +	}
> +}
> +
> +/* Convert current back-ground scrub rate into byte/sec bandwith */
> +static int cpc925_get_sdram_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
> +{
> +	struct cpc925_mc_pdata *pdata = mci->pvt_info;
> +	u32 mscr;
> +	u8 si;
> +
> +	mscr = __raw_readl(pdata->vbase + REG_MSCR_OFFSET);
> +	si = (mscr & MSCR_SI_MASK) >> MSCR_SI_SHIFT;
> +
> +	debugf0("%s, Mem Scrub Ctrl Register 0x%x\n", __func__, mscr);
> +
> +	if (((mscr & MSCR_SCRUB_MOD_MASK) != MSCR_BACKGR_SCRUB) ||
> +	    (si == 0)) {
> +		cpc925_mc_printk(mci, KERN_INFO, "Scrub mode not enabled\n");
> +		*bw = 0;
> +	} else
> +		*bw = CPC925_SCRUB_BLOCK_SIZE * 0xFA67 / si;
> +
> +	return 0;
> +}
> +
> +/* Return 0 for single channel; 1 for dual channel */
> +static int cpc925_mc_get_channels(void __iomem *vbase)
> +{
> +	int dual = 0;
> +	u32 mbcr;
> +
> +	mbcr = __raw_readl(vbase + REG_MBCR_OFFSET);
> +
> +	/*
> +	 * Dual channel only when 128-bit wide physical bus
> +	 * and 128-bit configuration.
> +	 */
> +	if (((mbcr & MBCR_64BITCFG_MASK) == 0) &&
> +	    ((mbcr & MBCR_64BITBUS_MASK) == 0))
> +		dual = 1;
> +
> +	debugf0("%s: %s channel\n", __func__,
> +		(dual > 0) ? "Dual" : "Single");
> +
> +	return dual;
> +}
> +
> +static int __devinit cpc925_probe(struct platform_device *pdev)
> +{
> +	struct mem_ctl_info *mci;
> +	void __iomem *vbase;
> +	struct cpc925_mc_pdata *pdata;
> +	struct resource *r;
> +	int res = 0, nr_channels;
> +
> +	debugf0("%s: %s platform device found!\n", __func__, pdev->name);
> +
> +	if (!devres_open_group(&pdev->dev, cpc925_probe, GFP_KERNEL)) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!r) {
> +		cpc925_printk(KERN_ERR, "Unable to get resource\n");
> +		res = -ENOENT;
> +		goto err1;
> +	}
> +
> +	if (!devm_request_mem_region(&pdev->dev,
> +				     r->start,
> +				     r->end - r->start + 1,
> +				     pdev->name)) {
> +		cpc925_printk(KERN_ERR, "Unable to request mem region\n");
> +		res = -EBUSY;
> +		goto err1;
> +	}
> +
> +	vbase = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1);
> +	if (!vbase) {
> +		cpc925_printk(KERN_ERR, "Unable to ioremap device\n");
> +		res = -ENOMEM;
> +		goto err2;
> +	}
> +
> +	nr_channels = cpc925_mc_get_channels(vbase);
> +	mci = edac_mc_alloc(sizeof(struct cpc925_mc_pdata),
> +			CPC925_NR_CSROWS, nr_channels + 1, edac_mc_idx);
> +	if (!mci) {
> +		cpc925_printk(KERN_ERR, "No memory for mem_ctl_info\n");
> +		res = -ENOMEM;
> +		goto err2;
> +	}
> +
> +	pdata = mci->pvt_info;
> +	pdata->vbase = vbase;
> +	pdata->edac_idx = edac_mc_idx++;
> +	pdata->name = pdev->name;
> +
> +	mci->dev = &pdev->dev;
> +	platform_set_drvdata(pdev, mci);
> +	mci->dev_name = dev_name(&pdev->dev);
> +	mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
> +	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
> +	mci->edac_cap = EDAC_FLAG_SECDED;
> +	mci->mod_name = CPC925_EDAC_MOD_STR;
> +	mci->mod_ver = CPC925_EDAC_REVISION;
> +	mci->ctl_name = pdev->name;
> +
> +	if (edac_op_state == EDAC_OPSTATE_POLL)
> +		mci->edac_check = cpc925_mc_check;
> +
> +	mci->ctl_page_to_phys = NULL;
> +	mci->scrub_mode = SCRUB_SW_SRC;
> +	mci->set_sdram_scrub_rate = NULL;
> +	mci->get_sdram_scrub_rate = cpc925_get_sdram_scrub_rate;
> +
> +	cpc925_init_csrows(mci);
> +
> +	/* Setup memory controller registers */
> +	cpc925_mc_init(mci);
> +
> +	if (edac_mc_add_mc(mci) > 0) {
> +		cpc925_mc_printk(mci, KERN_ERR, "Failed edac_mc_add_mc()\n");
> +		goto err3;
> +	}
> +
> +	cpc925_add_edac_devices(vbase);
> +
> +	/* get this far and it's successful */
> +	debugf0("%s: success\n", __func__);
> +
> +	res = 0;
> +	goto out;
> +
> +err3:
> +	cpc925_mc_exit(mci);
> +	edac_mc_free(mci);
> +err2:
> +	devm_release_mem_region(&pdev->dev, r->start, r->end-r->start+1);
> +err1:
> +	devres_release_group(&pdev->dev, cpc925_probe);
> +out:
> +	return res;
> +}
> +
> +static int cpc925_remove(struct platform_device *pdev)
> +{
> +	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
> +
> +	/*
> +	 * Delete common edac devices before edac mc, because
> +	 * the former share the MMIO of the latter.
> +	 */
> +	cpc925_del_edac_devices();
> +	cpc925_mc_exit(mci);
> +
> +	edac_mc_del_mc(&pdev->dev);
> +	edac_mc_free(mci);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver cpc925_edac_driver = {
> +	.probe = cpc925_probe,
> +	.remove = cpc925_remove,
> +	.driver = {
> +		   .name = "cpc925_edac",
> +	}
> +};
> +
> +static int __init cpc925_edac_init(void)
> +{
> +	int ret = 0;
> +
> +	printk(KERN_INFO "IBM CPC925 EDAC driver " CPC925_EDAC_REVISION "\n");
> +	printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc\n");
> +
> +	/* Only support POLL mode so far */
> +	edac_op_state = EDAC_OPSTATE_POLL;
> +
> +	ret = platform_driver_register(&cpc925_edac_driver);
> +	if (ret) {
> +		printk(KERN_WARNING "Failed to register %s\n",
> +			CPC925_EDAC_MOD_STR);
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit cpc925_edac_exit(void)
> +{
> +	platform_driver_unregister(&cpc925_edac_driver);
> +}
> +
> +module_init(cpc925_edac_init);
> +module_exit(cpc925_edac_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>");
> +MODULE_DESCRIPTION("IBM CPC925 Bridge and MC EDAC kernel module");

^ permalink raw reply

* RE: USB does not work on mpc8347
From: Li Yang-R58472 @ 2009-04-30  3:06 UTC (permalink / raw)
  To: yamazaki; +Cc: linuxppc-dev
In-Reply-To: <200904300112.AA00771@cj3020122-b.jcom.home.ne.jp>

>Hi Leo
>
>USB work on mpc8347 now.
>Thank you for your help.
>The cause was Clock signal was not connected to USB.
>
>And I have the other problem.
>I need to java run time environment on mpc8347.
>I do not know where i can get it.
>Could you tell me if you know.

I have no particular knowledge about installing JRE on Linux.  Google
leads me to this link which might be helpful: http://openjdk.java.net/

- Leo

^ permalink raw reply

* Re: Need a patch tested on a windtunnel powermac
From: Josh Boyer @ 2009-04-30  0:24 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: Jean Delvare, linuxppc-dev
In-Reply-To: <18936.58163.872136.772820@cargo.ozlabs.ibm.com>

On Thu, Apr 30, 2009 at 09:30:59AM +1000, Paul Mackerras wrote:
>Does anyone here still have a "windtunnel" powermac (PowerMac3,6)
>machine running Linux?  We need someone to test Jean Delvare's patch
>to the therm_windtunnel driver.  The patch is at:
>
>http://patchwork.ozlabs.org/patch/26095/
>
>I don't have a windtunnel machine myself - I have a PowerMac3,5 but
>not a PowerMac3,6.

I might have one.  I'll pull the G4 I have out of it's box and check tomorrow.

josh

^ permalink raw reply

* Re: [PATCH 2/2] powerpc: Move VSX load/stores into ppc-opcode.h
From: Michael Neuling @ 2009-04-30  0:24 UTC (permalink / raw)
  To: Kumar Gala; +Cc: Paul Mackerras, Milton Miller, linuxppc-dev
In-Reply-To: <1271A9EB-6489-4A72-B3A0-0C36AEE554CB@kernel.crashing.org>

> > Index: linux-2.6-ozlabs/arch/powerpc/include/asm/ppc-opcode.h
> > ===================================================================
> > --- linux-2.6-ozlabs.orig/arch/powerpc/include/asm/ppc-opcode.h
> > +++ linux-2.6-ozlabs/arch/powerpc/include/asm/ppc-opcode.h
> > @@ -49,6 +49,7 @@
> > /* macros to insert fields into opcodes */
> > #define __PPC_RA(a)	(((a) & 0x1f) << 16)
> > #define __PPC_RB(b)	(((b) & 0x1f) << 11)
> > +#define __PPC_XS(s)	((((s) & 0x1f) << 21) | (((s) & 0x20) >> 5))
> > #define __PPC_T_TLB(t)	(((t) & 0x3) << 21)
> > #define __PPC_WC(w)	(((w) & 0x3) << 21)
> >
> > @@ -70,4 +71,12 @@
> > #define PPC_WAIT(w)		stringify_in_c(.long PPC_INST_WAIT | \
> > 					__PPC_WC(w))
> >
> > +/*
> > + * Define what the VSX XX1 form instructions will look like, then add
> > + * the 128 bit load store instructions based on that.
> > + */
> > +#define VSX_XX1(s, a, b)	(__PPC_XS(s) | __PPC_RA(a) | __PPC_RB(b))
> > +#define STXVD2X(s, a, b)	.long (0x7c000798 | VSX_XX1((s), (a), (b)))
> > +#define LXVD2X(s, a, b)		.long (0x7c000698 | VSX_XX1((s), (a), (
b)))
> > +
> 
> previous gripe of matching style of file for the .long 0x7c000798

Sorry, yes.  

I also didn't sort the PPC_INST_* alphabetically for the tlbie patch.

Mikey

^ permalink raw reply

* Need a patch tested on a windtunnel powermac
From: Paul Mackerras @ 2009-04-29 23:30 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Jean Delvare

Does anyone here still have a "windtunnel" powermac (PowerMac3,6)
machine running Linux?  We need someone to test Jean Delvare's patch
to the therm_windtunnel driver.  The patch is at:

http://patchwork.ozlabs.org/patch/26095/

I don't have a windtunnel machine myself - I have a PowerMac3,5 but
not a PowerMac3,6.

Paul.

^ permalink raw reply

* Re: [PATCH 1/2] Add support for Designware SATA controller driver
From: Mark Miesfeld @ 2009-04-29 23:35 UTC (permalink / raw)
  To: Feng Kan; +Cc: linuxppc-dev, linux-ide
In-Reply-To: <20090429232818.A15B683420EE@gemini.denx.de>

Feng,

Can you just insert my Signed off by line, using Mark Miesfeld
<miesfeld@gmail.com>

I always goof up the formatting.  If that is okay with you and meets
your ethical standards.

--
Mark Miesfeld
miesfeld@gmail.com

On Wed, Apr 29, 2009 at 4:28 PM, Wolfgang Denk <wd@denx.de> wrote:
> Dear Feng Kan,
>
> In message <1241042419-19774-1-git-send-email-fkan@amcc.com> you wrote:
>> Signed-off-by: Feng Kan <fkan@amcc.com>
>> ---
>> =A0drivers/ata/Kconfig =A0 =A0| =A0 76 +-
>> =A0drivers/ata/Makefile =A0 | =A0 =A01 +
>> =A0drivers/ata/sata_dwc.c | 2047 +++++++++++++++++++++++++++++++++++++++=
+++++++++
>> =A03 files changed, 2091 insertions(+), 33 deletions(-)
>> =A0create mode 100644 drivers/ata/sata_dwc.c
>>
>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>> index 0bcf264..5321e47 100644
>> --- a/drivers/ata/Kconfig
>> +++ b/drivers/ata/Kconfig
>> @@ -72,56 +72,66 @@ config SATA_FSL
>>
>> =A0 =A0 =A0 =A0 If unsure, say N.
>>
>> -config ATA_SFF
>> - =A0 =A0 bool "ATA SFF support"
>> - =A0 =A0 default y
>> +config SATA_DWC
>> + =A0 =A0 tristate "DesignWare Cores SATA support"
>> + =A0 =A0 depends on 460EX
>> =A0 =A0 =A0 help
>> - =A0 =A0 =A0 This option adds support for ATA controllers with SFF
>> - =A0 =A0 =A0 compliant or similar programming interface.
>> + =A0 =A0 =A0 This option enables support for the Synopsys DesignWare Co=
res SATA
>> + =A0 =A0 =A0 controller.
>> + =A0 =A0 =A0 It can be found on the AMCC 460EX.
>>
>> - =A0 =A0 =A0 SFF is the legacy IDE interface that has been around since
>> - =A0 =A0 =A0 the dawn of time. =A0Almost all PATA controllers have an
>> - =A0 =A0 =A0 SFF interface. =A0Many SATA controllers have an SFF interf=
ace
>> - =A0 =A0 =A0 when configured into a legacy compatibility mode.
>> + =A0 =A0 =A0 If unsure, say N.
>>
>> - =A0 =A0 =A0 For users with exclusively modern controllers like AHCI,
>> - =A0 =A0 =A0 Silicon Image 3124, or Marvell 6440, you may choose to
>> - =A0 =A0 =A0 disable this uneeded SFF support.
>> +config ATA_SFF
>> +bool "ATA SFF support"
>> +default y
>> +help
>> + =A0This option adds support for ATA controllers with SFF
>> + =A0compliant or similar programming interface.
>>
>> - =A0 =A0 =A0 If unsure, say Y.
>> + =A0SFF is the legacy IDE interface that has been around since
>> + =A0the dawn of time. =A0Almost all PATA controllers have an
>> + =A0SFF interface. =A0Many SATA controllers have an SFF interface
>> + =A0when configured into a legacy compatibility mode.
>> +
>> + =A0For users with exclusively modern controllers like AHCI,
>> + =A0Silicon Image 3124, or Marvell 6440, you may choose to
>> + =A0disable this uneeded SFF support.
>> +
>> + =A0If unsure, say Y.
>
> Why are you reformatting exiting, correct help text, into brokenness?
>
> ...
>> +static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
>> +{
> ...
>> + =A0 =A0 =A0 =A0 =A0 =A0 dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, pr=
otocol: %s\n",
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 __func__, prot_2_txt(qc->tf.pr=
otocol));
>> +DRVSTILLBUSY:
> ^^^^^^^^^^^^^^^^
> ...
>> +PROCESS: =A0/* process completed commands */
> ^^^^^^^^^^^
>
> ...
>> +STILLBUSY:
> ^^^^^^^^^^^^^
> ...
>> +DONE:
> ^^^^^^^^
>
> etc. =A0Please do not use all-caps identifier names (not even for
> labels).
>
> ...
>> +/**********************************************************************=
*********
>> + * Function : sata_dwc_port_start
>> + * arguments : struct ata_ioports *port
>> + * Return value : returns 0 if success, error code otherwise
>> + * This function allocates the scatter gather LLI table for AHB DMA
>> + **********************************************************************=
********/
>
> Here and elsewhere: incorrect multiline comment style.
>
> ...
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>");
>
> Should not Mark add his Signed-off-by: line, too?
>
>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH, =A0 =A0 MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> Conscious is when you are aware of something, and conscience is =A0when
> you wish you weren't.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ide" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 1/2] Add support for Designware SATA controller driver
From: Wolfgang Denk @ 2009-04-29 23:28 UTC (permalink / raw)
  To: Feng Kan; +Cc: linuxppc-dev, linux-ide
In-Reply-To: <1241042419-19774-1-git-send-email-fkan@amcc.com>

Dear Feng Kan,

In message <1241042419-19774-1-git-send-email-fkan@amcc.com> you wrote:
> Signed-off-by: Feng Kan <fkan@amcc.com>
> ---
>  drivers/ata/Kconfig    |   76 +-
>  drivers/ata/Makefile   |    1 +
>  drivers/ata/sata_dwc.c | 2047 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 2091 insertions(+), 33 deletions(-)
>  create mode 100644 drivers/ata/sata_dwc.c
> 
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index 0bcf264..5321e47 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -72,56 +72,66 @@ config SATA_FSL
>  
>  	  If unsure, say N.
>  
> -config ATA_SFF
> -	bool "ATA SFF support"
> -	default y
> +config SATA_DWC
> +	tristate "DesignWare Cores SATA support"
> +	depends on 460EX
>  	help
> -	  This option adds support for ATA controllers with SFF
> -	  compliant or similar programming interface.
> +	  This option enables support for the Synopsys DesignWare Cores SATA
> +	  controller.
> +	  It can be found on the AMCC 460EX.
>  
> -	  SFF is the legacy IDE interface that has been around since
> -	  the dawn of time.  Almost all PATA controllers have an
> -	  SFF interface.  Many SATA controllers have an SFF interface
> -	  when configured into a legacy compatibility mode.
> +	  If unsure, say N.
>  
> -	  For users with exclusively modern controllers like AHCI,
> -	  Silicon Image 3124, or Marvell 6440, you may choose to
> -	  disable this uneeded SFF support.
> +config ATA_SFF
> +bool "ATA SFF support"
> +default y
> +help
> +  This option adds support for ATA controllers with SFF
> +  compliant or similar programming interface.
>  
> -	  If unsure, say Y.
> +  SFF is the legacy IDE interface that has been around since
> +  the dawn of time.  Almost all PATA controllers have an
> +  SFF interface.  Many SATA controllers have an SFF interface
> +  when configured into a legacy compatibility mode.
> +
> +  For users with exclusively modern controllers like AHCI,
> +  Silicon Image 3124, or Marvell 6440, you may choose to
> +  disable this uneeded SFF support.
> +
> +  If unsure, say Y.

Why are you reformatting exiting, correct help text, into brokenness?

...
> +static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
> +{
...
> +		dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
> +			__func__, prot_2_txt(qc->tf.protocol));
> +DRVSTILLBUSY:
^^^^^^^^^^^^^^^^
...
> +PROCESS:  /* process completed commands */
^^^^^^^^^^^

...
> +STILLBUSY:
^^^^^^^^^^^^^
...
> +DONE:
^^^^^^^^

etc.  Please do not use all-caps identifier names (not even for
labels).

...
> +/*******************************************************************************
> + * Function : sata_dwc_port_start
> + * arguments : struct ata_ioports *port
> + * Return value : returns 0 if success, error code otherwise
> + * This function allocates the scatter gather LLI table for AHB DMA
> + ******************************************************************************/

Here and elsewhere: incorrect multiline comment style.

...
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>");

Should not Mark add his Signed-off-by: line, too?


Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Conscious is when you are aware of something, and conscience is  when
you wish you weren't.

^ permalink raw reply

* Re: [PATCH v6] introduce macro spin_event_timeout()
From: Timur Tabi @ 2009-04-29 22:48 UTC (permalink / raw)
  To: linuxppc-dev, galak, benh, jwboyer, scottwood
In-Reply-To: <1240859144-20893-1-git-send-email-timur@freescale.com>

On Mon, Apr 27, 2009 at 2:05 PM, Timur Tabi <timur@freescale.com> wrote:
> The macro spin_event_timeout() takes a condition and timeout value
> (in microseconds) as parameters. =A0It spins until either the condition i=
s true
> or the timeout expires. =A0It returns the result of the condition when th=
e loop
> was terminated.
>
> This primary purpose of this macro is to poll on a hardware register unti=
l a
> status bit changes. =A0The timeout ensures that the loop still terminates=
 if the
> bit doesn't change as expected. =A0This macro makes it easier for driver
> developers to perform this kind of operation properly.

Any comments on this patch?  I'd like to see this macro added to 2.6.31.

--=20
Timur Tabi
Linux kernel developer at Freescale

^ permalink raw reply

* [PATCH 1/2] Add support for Designware SATA controller driver
From: Feng Kan @ 2009-04-29 22:00 UTC (permalink / raw)
  To: linuxppc-dev, linux-ide; +Cc: Feng Kan

Signed-off-by: Feng Kan <fkan@amcc.com>
---
 drivers/ata/Kconfig    |   76 +-
 drivers/ata/Makefile   |    1 +
 drivers/ata/sata_dwc.c | 2047 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2091 insertions(+), 33 deletions(-)
 create mode 100644 drivers/ata/sata_dwc.c

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 0bcf264..5321e47 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -72,56 +72,66 @@ config SATA_FSL
 
 	  If unsure, say N.
 
-config ATA_SFF
-	bool "ATA SFF support"
-	default y
+config SATA_DWC
+	tristate "DesignWare Cores SATA support"
+	depends on 460EX
 	help
-	  This option adds support for ATA controllers with SFF
-	  compliant or similar programming interface.
+	  This option enables support for the Synopsys DesignWare Cores SATA
+	  controller.
+	  It can be found on the AMCC 460EX.
 
-	  SFF is the legacy IDE interface that has been around since
-	  the dawn of time.  Almost all PATA controllers have an
-	  SFF interface.  Many SATA controllers have an SFF interface
-	  when configured into a legacy compatibility mode.
+	  If unsure, say N.
 
-	  For users with exclusively modern controllers like AHCI,
-	  Silicon Image 3124, or Marvell 6440, you may choose to
-	  disable this uneeded SFF support.
+config ATA_SFF
+bool "ATA SFF support"
+default y
+help
+  This option adds support for ATA controllers with SFF
+  compliant or similar programming interface.
 
-	  If unsure, say Y.
+  SFF is the legacy IDE interface that has been around since
+  the dawn of time.  Almost all PATA controllers have an
+  SFF interface.  Many SATA controllers have an SFF interface
+  when configured into a legacy compatibility mode.
+
+  For users with exclusively modern controllers like AHCI,
+  Silicon Image 3124, or Marvell 6440, you may choose to
+  disable this uneeded SFF support.
+
+  If unsure, say Y.
 
 if ATA_SFF
 
 config SATA_SVW
-	tristate "ServerWorks Frodo / Apple K2 SATA support"
-	depends on PCI
-	help
-	  This option enables support for Broadcom/Serverworks/Apple K2
-	  SATA support.
+tristate "ServerWorks Frodo / Apple K2 SATA support"
+depends on PCI
+help
+  This option enables support for Broadcom/Serverworks/Apple K2
+  SATA support.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config ATA_PIIX
-	tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
-	depends on PCI
-	help
-	  This option enables support for ICH5/6/7/8 Serial ATA
-	  and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
-	  host controllers.
+tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
+depends on PCI
+help
+  This option enables support for ICH5/6/7/8 Serial ATA
+  and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
+  host controllers.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config SATA_MV
-	tristate "Marvell SATA support"
-	help
-	  This option enables support for the Marvell Serial ATA family.
-	  Currently supports 88SX[56]0[48][01] PCI(-X) chips,
-	  as well as the newer [67]042 PCI-X/PCIe and SOC devices.
+tristate "Marvell SATA support"
+help
+  This option enables support for the Marvell Serial ATA family.
+  Currently supports 88SX[56]0[48][01] PCI(-X) chips,
+  as well as the newer [67]042 PCI-X/PCIe and SOC devices.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config SATA_NV
-	tristate "NVIDIA SATA support"
+tristate "NVIDIA SATA support"
 	depends on PCI
 	help
 	  This option enables support for NVIDIA Serial ATA.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 7f1ecf9..3d41fc7 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SATA_MV)		+= sata_mv.o
 obj-$(CONFIG_SATA_INIC162X)	+= sata_inic162x.o
 obj-$(CONFIG_PDC_ADMA)		+= pdc_adma.o
 obj-$(CONFIG_SATA_FSL)		+= sata_fsl.o
+obj-$(CONFIG_SATA_DWC)          += sata_dwc.o
 
 obj-$(CONFIG_PATA_ALI)		+= pata_ali.o
 obj-$(CONFIG_PATA_AMD)		+= pata_amd.o
diff --git a/drivers/ata/sata_dwc.c b/drivers/ata/sata_dwc.c
new file mode 100644
index 0000000..1a6fce0
--- /dev/null
+++ b/drivers/ata/sata_dwc.c
@@ -0,0 +1,2047 @@
+/*
+ * drivers/ata/sata_dwc.c
+ *
+ * Synopsys DesignWare Cores (DWC) SATA host driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@amcc.com>
+ *
+ * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de>
+ * Copyright 2008 DENX Software Engineering
+ *
+ * Based on versions provided by AMCC and Synopsys which are:
+ *          Copyright 2006 Applied Micro Circuits Corporation
+ *          COPYRIGHT (C) 2005  SYNOPSYS, INC.  ALL RIGHTS RESERVED
+ *
+ * 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.
+ *
+ */
+#ifdef CONFIG_SATA_DWC_DEBUG
+#define DEBUG
+#endif
+
+#ifdef CONFIG_SATA_DWC_VDEBUG
+#define VERBOSE_DEBUG
+#define DEBUG_NCQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/libata.h>
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+
+#define DRV_NAME        "sata-dwc"
+#define DRV_VERSION     "1.0"
+
+/* SATA DMA driver Globals */
+#define DMA_NUM_CHANS			1
+#define DMA_NUM_CHAN_REGS		8
+
+/* SATA DMA Register definitions */
+#define AHB_DMA_BRST_DFLT		64	/* 16 data items burst length */
+
+struct dmareg {
+	u32 low;		/* Low bits 0-31 */
+	u32 high;		/* High bits 32-63 */
+};
+
+/* DMA Per Channel registers */
+
+struct dma_chan_regs {
+	struct dmareg sar;	/* Source Address */
+	struct dmareg dar;	/* Destination address */
+	struct dmareg llp;	/* Linked List Pointer */
+	struct dmareg ctl;	/* Control */
+	struct dmareg sstat;	/* Source Status not implemented in core */
+	struct dmareg dstat;	/* Destination Status not implemented in core */
+	struct dmareg sstatar;	/* Source Status Address not impl in core */
+	struct dmareg dstatar;	/* Destination Status Address not implemented */
+	struct dmareg cfg;	/* Config */
+	struct dmareg sgr;	/* Source Gather */
+	struct dmareg dsr;	/* Destination Scatter */
+};
+
+/* Generic Interrupt Registers */
+struct dma_interrupt_regs {
+	struct dmareg tfr;	/* Transfer Interrupt */
+	struct dmareg block;	/* Block Interrupt */
+	struct dmareg srctran;	/* Source Transfer Interrupt */
+	struct dmareg dsttran;	/* Dest Transfer Interrupt */
+	struct dmareg error;	/* Error */
+};
+
+struct ahb_dma_regs {
+	struct dma_chan_regs	chan_regs[DMA_NUM_CHAN_REGS];
+	struct dma_interrupt_regs interrupt_raw;	/* Raw Interrupt */
+	struct dma_interrupt_regs interrupt_status;	/* Interrupt Status */
+	struct dma_interrupt_regs interrupt_mask;	/* Interrupt Mask */
+	struct dma_interrupt_regs interrupt_clear;	/* Interrupt Clear */
+	struct dmareg		statusInt;		/* Interrupt combined */
+	struct dmareg		rq_srcreg;		/* Src Trans Req */
+	struct dmareg		rq_dstreg;		/* Dst Trans Req */
+	struct dmareg		rq_sgl_srcreg;		/* Sngl Src Trans Req */
+	struct dmareg		rq_sgl_dstreg;		/* Sngl Dst Trans Req */
+	struct dmareg		rq_lst_srcreg;		/* Last Src Trans Req */
+	struct dmareg		rq_lst_dstreg;		/* Last Dst Trans Req */
+	struct dmareg		dma_cfg;		/* DMA Config */
+	struct dmareg		dma_chan_en;		/* DMA Channel Enable */
+	struct dmareg		dma_id;			/* DMA ID */
+	struct dmareg		dma_test;		/* DMA Test */
+	struct dmareg		res1;			/* reserved */
+	struct dmareg		res2;			/* reserved */
+
+	/* DMA Comp Params
+	 * Param 6 = dma_param[0], Param 5 = dma_param[1],
+	 * Param 4 = dma_param[2] ...
+	 */
+	struct dmareg		dma_params[6];
+};
+
+/* Data structure for linked list item */
+struct lli {
+	u32		sar;		/* Source Address */
+	u32		dar;		/* Destination address */
+	u32		llp;		/* Linked List Pointer */
+	struct dmareg	ctl;		/* Control */
+	struct dmareg	dstat;		/* Destination Status */
+};
+
+#define SATA_DWC_DMAC_LLI_SZ		(sizeof(struct lli))
+#define SATA_DWC_DMAC_LLI_NUM		256
+#define SATA_DWC_DMAC_TWIDTH_BYTES	4
+#define SATA_DWC_DMAC_LLI_TBL_SZ	\
+	(SATA_DWC_DMAC_LLI_SZ * SATA_DWC_DMAC_LLI_NUM)
+#define SATA_DWC_DMAC_CTRL_TSIZE_MAX	\
+	(0x00000800 * SATA_DWC_DMAC_TWIDTH_BYTES)
+
+/* DMA Register Operation Bits */
+#define DMA_EN			0x00000001		/* Enable AHB DMA */
+#define DMA_CHANNEL(ch)		(0x00000001 << (ch))	/* Select channel */
+#define DMA_ENABLE_CHAN(ch)	((0x00000001 << (ch)) |			\
+				 ((0x000000001 << (ch)) << 8))
+#define DMA_DISABLE_CHAN(ch)	(0x00000000 | ((0x000000001 << (ch)) << 8))
+
+/* Channel Control Register */
+#define DMA_CTL_BLK_TS(size)	((size) & 0x000000FFF)	/* Blk Transfer size */
+#define DMA_CTL_LLP_SRCEN	0x10000000	/* Blk chain enable Src */
+#define DMA_CTL_LLP_DSTEN	0x08000000	/* Blk chain enable Dst */
+/*
+ * This define is used to set block chaining disabled in the control low
+ * register.  It is already in little endian format so it can be &'d dirctly.
+ * It is essentially: cpu_to_le32(~(DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN))
+ */
+#define DMA_CTL_LLP_DISABLE_LE32 0xffffffe7
+#define DMA_CTL_SMS(num)	((num & 0x3) << 25)	/*Src Master Select*/
+#define DMA_CTL_DMS(num)	((num & 0x3) << 23)	/*Dst Master Select*/
+#define DMA_CTL_TTFC(type)	((type & 0x7) << 20)	/*Type&Flow cntr*/
+#define DMA_CTL_TTFC_P2M_DMAC	0x00000002		/*Per mem,DMAC cntr*/
+#define DMA_CTL_TTFC_M2P_PER	0x00000003		/*Mem per,peri cntr*/
+#define DMA_CTL_SRC_MSIZE(size)	((size & 0x7) << 14)	/*Src Burst Len*/
+#define DMA_CTL_DST_MSIZE(size)	((size & 0x7) << 11)	/*Dst Burst Len*/
+#define DMA_CTL_SINC_INC	0x00000000		/*Src addr incr*/
+#define DMA_CTL_SINC_DEC	0x00000200
+#define DMA_CTL_SINC_NOCHANGE	0x00000400
+#define DMA_CTL_DINC_INC	0x00000000		/*Dst addr incr*/
+#define DMA_CTL_DINC_DEC	0x00000080
+#define DMA_CTL_DINC_NOCHANGE	0x00000100
+#define DMA_CTL_SRC_TRWID(size)	((size & 0x7) << 4)	/*Src Trnsfr Width*/
+#define DMA_CTL_DST_TRWID(size)	((size & 0x7) << 1)	/*Dst Trnsfr Width*/
+#define DMA_CTL_INT_EN		0x00000001		/*Interrupt Enable*/
+
+/* Channel Configuration Register high bits */
+#define DMA_CFG_FCMOD_REQ	0x00000001		/*Flow cntrl req*/
+#define DMA_CFG_PROTCTL		(0x00000003 << 2)	/*Protection cntrl*/
+
+/* Channel Configuration Register low bits */
+#define DMA_CFG_RELD_DST	0x80000000		/*Reload Dst/Src Addr*/
+#define DMA_CFG_RELD_SRC	0x40000000
+#define DMA_CFG_HS_SELSRC	0x00000800		/*SW hndshk Src/Dst*/
+#define DMA_CFG_HS_SELDST	0x00000400
+#define DMA_CFG_FIFOEMPTY       (0x00000001 << 9)	/*FIFO Empty bit*/
+
+/* Assign hardware handshaking interface (x) to dst / sre peripheral */
+#define DMA_CFG_HW_HS_DEST(int_num)	((int_num & 0xF) << 11)
+#define DMA_CFG_HW_HS_SRC(int_num)	((int_num & 0xF) << 7)
+
+/* Channel Linked List Pointer Register */
+#define DMA_LLP_LMS(addr, master)	(((addr) & 0xfffffffc) | (master))
+#define DMA_LLP_AHBMASTER1		0	/* List Master Select */
+#define DMA_LLP_AHBMASTER2		1
+
+#define SATA_DWC_MAX_PORTS	1
+
+#define SATA_DWC_SCR_OFFSET	0x24
+#define SATA_DWC_REG_OFFSET	0x64
+
+/* DWC SATA Registers */
+struct sata_dwc_regs {
+	u32 fptagr;		/* 1st party DMA tag */
+	u32 fpbor;		/* 1st party DMA buffer offset */
+	u32 fptcr;		/* 1st party DMA Xfr count */
+	u32 dmacr;		/* DMA Control */
+	u32 dbtsr;		/* DMA Burst Transac size */
+	u32 intpr;		/* Interrupt Pending */
+	u32 intmr;		/* Interrupt Mask */
+	u32 errmr;		/* Error Mask */
+	u32 llcr;		/* Link Layer Control */
+	u32 phycr;		/* PHY Control */
+	u32 physr;		/* PHY Status */
+	u32 rxbistpd;		/* Recvd BIST pattern def register */
+	u32 rxbistpd1;		/* Recvd BIST data dword1 */
+	u32 rxbistpd2;		/* Recvd BIST pattern data dword2 */
+	u32 txbistpd;		/* Trans BIST pattern def register */
+	u32 txbistpd1;		/* Trans BIST data dword1 */
+	u32 txbistpd2;		/* Trans BIST data dword2 */
+	u32 bistcr;		/* BIST Control Register */
+	u32 bistfctr;		/* BIST FIS Count Register */
+	u32 bistsr;		/* BIST Status Register */
+	u32 bistdecr;		/* BIST Dword Error count register */
+	u32 res[15];		/* Reserved locations */
+	u32 testr;		/* Test Register */
+	u32 versionr;		/* Version Register */
+	u32 idr;		/* ID Register */
+	u32 unimpl[192];	/* Unimplemented */
+	u32 dmadr[256];	/* FIFO Locations in DMA Mode */
+};
+
+#define SCR_SCONTROL_DET_ENABLE		0x00000001
+#define SCR_SSTATUS_DET_PRESENT		0x00000001
+#define SCR_SERROR_DIAG_X		0x04000000
+
+/* DWC SATA Register Operations */
+#define	SATA_DWC_TXFIFO_DEPTH		0x01FF
+#define	SATA_DWC_RXFIFO_DEPTH		0x01FF
+
+#define SATA_DWC_DMACR_TMOD_TXCHEN	0x00000004
+#define	SATA_DWC_DMACR_TXCHEN		(0x00000001 | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define	SATA_DWC_DMACR_RXCHEN		(0x00000002 | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_TX_CLEAR(v)	(((v) & ~SATA_DWC_DMACR_TXCHEN) | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_RX_CLEAR(v)	(((v) & ~SATA_DWC_DMACR_RXCHEN) | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_TXRXCH_CLEAR	SATA_DWC_DMACR_TMOD_TXCHEN
+
+#define SATA_DWC_DBTSR_MWR(size)	((size/4) & \
+						SATA_DWC_TXFIFO_DEPTH)
+#define SATA_DWC_DBTSR_MRD(size)	(((size/4) & \
+						SATA_DWC_RXFIFO_DEPTH) << 16)
+#define	SATA_DWC_INTPR_DMAT		0x00000001
+#define SATA_DWC_INTPR_NEWFP		0x00000002
+#define SATA_DWC_INTPR_PMABRT		0x00000004
+#define SATA_DWC_INTPR_ERR		0x00000008
+#define SATA_DWC_INTPR_NEWBIST		0x00000010
+#define SATA_DWC_INTPR_IPF		0x10000000
+#define	SATA_DWC_INTMR_DMATM		0x00000001
+#define SATA_DWC_INTMR_NEWFPM		0x00000002
+#define SATA_DWC_INTMR_PMABRTM		0x00000004
+#define SATA_DWC_INTMR_ERRM		0x00000008
+#define SATA_DWC_INTMR_NEWBISTM		0x00000010
+#define SATA_DWC_LLCR_SCRAMEN		0x00000001
+#define SATA_DWC_LLCR_DESCRAMEN		0x00000002
+#define SATA_DWC_LLCR_RPDEN		0x00000004
+
+/* This is all error bits, zero's are reserved fields. */
+#define SATA_DWC_SERROR_ERR_BITS	0x0FFF0F03
+
+#define SATA_DWC_SCR0_SPD_GET(v)	((v >> 4) & 0x0000000F)
+
+struct sata_dwc_device {
+	struct device		*dev;		/* generic device struct */
+	struct ata_probe_ent	*pe;		/* ptr to probe-ent */
+	struct ata_host		*host;
+	u8			*reg_base;
+	struct sata_dwc_regs	*sata_dwc_regs;	/* DW Synopsys SATA specific */
+	int			irq_dma;
+};
+
+#define SATA_DWC_QCMD_MAX	32
+
+struct sata_dwc_device_port {
+	struct sata_dwc_device	*hsdev;
+	int			cmd_issued[SATA_DWC_QCMD_MAX];
+	struct lli		*llit[SATA_DWC_QCMD_MAX];
+	dma_addr_t		llit_dma[SATA_DWC_QCMD_MAX];
+	u32			dma_chan[SATA_DWC_QCMD_MAX];
+	int			dma_pending[SATA_DWC_QCMD_MAX];
+};
+
+/*
+ * Commonly used DWC SATA driver Macros
+ */
+#define HSDEV_FROM_HOST(host)	((struct sata_dwc_device *) \
+					(host)->private_data)
+#define HSDEV_FROM_AP(ap)	((struct sata_dwc_device *) \
+					(ap)->host->private_data)
+#define HSDEVP_FROM_AP(ap)	((struct sata_dwc_device_port *) \
+					(ap)->private_data)
+#define HSDEV_FROM_QC(qc)	((struct sata_dwc_device *) \
+					(qc)->ap->host->private_data)
+#define HSDEV_FROM_HSDEVP(p)	((struct sata_dwc_device *) \
+					(hsdevp)->hsdev)
+
+enum {
+	SATA_DWC_CMD_ISSUED_NOT		= 0,
+	SATA_DWC_CMD_ISSUED_PEND	= 1,
+	SATA_DWC_CMD_ISSUED_EXEC	= 2,
+	SATA_DWC_CMD_ISSUED_NODATA	= 3,
+
+	SATA_DWC_DMA_PEND_NONE	= 0,
+	SATA_DWC_DMA_PEND_TX		= 1,
+	SATA_DWC_DMA_PEND_RX		= 2,
+};
+
+/*
+ * Globals
+ */
+static u32 sata_dwc_sactive_issued;	/* issued queued ops */
+static u32 sata_dwc_sactive_queued;	/* queued ops */
+static struct ahb_dma_regs *sata_dma_regs;
+static u32 dma_interrupt_count;
+static void *scr_addr_sstatus;
+static struct device *dwc_dev;
+
+/*
+ * Prototypes
+ */
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag);
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+				u32 check_status);
+static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status);
+static void sata_dwc_port_stop(struct ata_port *ap);
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag);
+
+static int dma_dwc_init(struct sata_dwc_device *hsdev, struct resource *res,
+			int irq);
+static void dma_dwc_exit(struct sata_dwc_device *hsdev);
+static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
+			      struct lli *lli, dma_addr_t dma_lli,
+			      void __iomem *addr, int dir);
+static void dma_dwc_xfer_start(int dma_ch);
+static void dma_dwc_terminate_dma(int dma_ch);
+
+static const char *dir_2_txt(enum dma_data_direction dir)
+{
+	switch (dir) {
+	case DMA_BIDIRECTIONAL:
+		return "bi";
+	case DMA_FROM_DEVICE:
+		return "from";
+	case DMA_TO_DEVICE:
+		return "to";
+	case DMA_NONE:
+		return "none";
+	default:
+		return "err";
+	}
+}
+
+static const char *prot_2_txt(enum ata_tf_protocols protocol)
+{
+	switch (protocol) {
+	case ATA_PROT_UNKNOWN:
+		return "unknown";
+	case ATA_PROT_NODATA:
+		return "nodata";
+	case ATA_PROT_PIO:
+		return "pio";
+	case ATA_PROT_DMA:
+		return "dma";
+	case ATA_PROT_NCQ:
+		return "ncq";
+	case ATAPI_PROT_PIO:
+		return "atapi pio";
+	case ATAPI_PROT_NODATA:
+		return "atapi nodata";
+	case ATAPI_PROT_DMA:
+		return "atapi dma";
+	default:
+		return "err";
+	}
+}
+
+inline const char *ata_cmd_2_txt(const struct ata_taskfile *tf)
+{
+	switch (tf->command) {
+	case ATA_CMD_CHK_POWER:
+		return "ATA_CMD_CHK_POWER";
+	case ATA_CMD_EDD:
+		return "ATA_CMD_EDD";
+	case ATA_CMD_FLUSH:
+		return "ATA_CMD_FLUSH";
+	case ATA_CMD_FLUSH_EXT:
+		return "ATA_CMD_FLUSH_EXT";
+	case ATA_CMD_ID_ATA:
+		return "ATA_CMD_ID_ATA";
+	case ATA_CMD_ID_ATAPI:
+		return "ATA_CMD_ID_ATAPI";
+	case ATA_CMD_FPDMA_READ:
+		return "ATA_CMD_FPDMA_READ";
+	case ATA_CMD_FPDMA_WRITE:
+		return "ATA_CMD_FPDMA_WRITE";
+	case ATA_CMD_READ:
+		return "ATA_CMD_READ";
+	case ATA_CMD_READ_EXT:
+		return "ATA_CMD_READ_EXT";
+	case ATA_CMD_WRITE:
+		return "ATA_CMD_WRITE";
+	case ATA_CMD_WRITE_EXT:
+		return "ATA_CMD_WRITE_EXT";
+	case ATA_CMD_PIO_READ:
+		return "ATA_CMD_PIO_READ";
+	case ATA_CMD_PIO_READ_EXT:
+		return "ATA_CMD_PIO_READ_EXT";
+	case ATA_CMD_PIO_WRITE:
+		return "ATA_CMD_PIO_WRITE";
+	case ATA_CMD_PIO_WRITE_EXT:
+		return "ATA_CMD_PIO_WRITE_EXT";
+	case ATA_CMD_SET_FEATURES:
+		return "ATA_CMD_SET_FEATURES";
+	case ATA_CMD_PACKET:
+		return "ATA_CMD_PACKET";
+	default:
+		return "ATA_CMD_???";
+	}
+}
+
+static void sata_dwc_tf_dump(struct ata_taskfile *tf)
+{
+	dev_vdbg(dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags: 0x%lx"
+			"device: %x\n", tf->command, prot_2_txt(tf->protocol),
+			tf->flags, tf->device);
+	dev_vdbg(dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x lbam:"
+			"0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal,
+			tf->lbam, tf->lbah);
+	dev_vdbg(dwc_dev, "hob_feature: 0x%02x hob_nsect: 0x%x hob_lbal: 0x%x "
+			"hob_lbam: 0x%x hob_lbah: 0x%x\n", tf->hob_feature,
+			tf->hob_nsect, tf->hob_lbal, tf->hob_lbam,
+			tf->hob_lbah);
+}
+
+/*******************************************************************************
+ * Function: get_burst_length_encode
+ * arguments: datalength: length in bytes of data
+ * returns value to be programmed in register corrresponding to data length
+ * This value is effectively the log(base 2) of the length
+ ******************************************************************************/
+static inline int get_burst_length_encode(int datalength)
+{
+	int items = datalength >> 2;	/* div by 4 to get lword count */
+
+	if (items >= 64)
+		return 5;
+
+	if (items >= 32)
+		return 4;
+
+	if (items >= 16)
+		return 3;
+
+	if (items >= 8)
+		return 2;
+
+	if (items >= 4)
+		return 1;
+
+	return 0;
+}
+
+static inline void clear_chan_interrupts(int c)
+{
+	out_le32(&(sata_dma_regs->interrupt_clear.tfr.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.block.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.srctran.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.dsttran.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.error.low), DMA_CHANNEL(c));
+}
+
+/*******************************************************************************
+ * Function: dma_request_channel
+ * arguments: None
+ * returns channel number if available else -1
+ * This function assigns the next available DMA channel from the list to the
+ * requester
+ ******************************************************************************/
+static int dma_request_channel(void)
+{
+	int i;
+	struct ahb_dma_regs *pdma;
+
+	pdma = sata_dma_regs;
+	for (i = 0; i < DMA_NUM_CHANS; i++) {
+		if (!(in_le32(&(pdma->dma_chan_en.low)) & DMA_CHANNEL(i)))
+			return i;
+	}
+
+	/* TODO this should maybe change to warn of info */
+	dev_err(dwc_dev, "%s NO channel chan_en: 0x%08x\n", __func__,
+		in_le32(&(pdma->dma_chan_en.low)));
+
+	return -1;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_interrupt
+ * arguments: irq, dev_id, pt_regs
+ * returns channel number if available else -1
+ * Interrupt Handler for DW AHB SATA DMA
+ ******************************************************************************/
+static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance)
+{
+	int chan;
+	u32 tfr_reg, err_reg;
+	unsigned long flags;
+	struct sata_dwc_device *hsdev =
+		(struct sata_dwc_device *)hsdev_instance;
+	struct ata_host *host = (struct ata_host *)hsdev->host;
+	struct ata_port *ap;
+	struct sata_dwc_device_port *hsdevp;
+	u8 tag = 0;
+	unsigned int port = 0;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	ap = host->ports[port];
+	hsdevp = HSDEVP_FROM_AP(ap);
+	tag = ap->link.active_tag;
+
+	tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low));
+	err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+
+	dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n",
+		tfr_reg, err_reg, hsdevp->dma_pending[tag], port);
+
+	for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+		/* Check for end-of-transfer interrupt. */
+		if (tfr_reg & DMA_CHANNEL(chan)) {
+			/* Each DMA command produces 2 interrupts.  Only
+			 * complete the command after both interrupts have been
+			 * seen. (See sata_dwc_isr())
+			 */
+			dma_interrupt_count++;
+
+			sata_dwc_clear_dmacr(hsdevp, tag);
+
+			if (hsdevp->dma_pending[tag] == 0) {
+				dev_err(ap->dev, "DMA not pending eot=0x%08x "
+					"err=0x%08x tag=0x%02x pending=%d\n",
+					tfr_reg, err_reg, tag,
+					hsdevp->dma_pending[tag]);
+			}
+
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+
+			/* Clear the interrupt */
+			out_le32(&(sata_dma_regs->interrupt_clear.tfr.low),
+				 DMA_CHANNEL(chan));
+		}
+
+		/* Check for error interrupt. */
+		if (err_reg & DMA_CHANNEL(chan)) {
+			/* TODO Need error handler ! */
+			dev_err(ap->dev, "error interrupt err_reg=0x%08x\n",
+					err_reg);
+
+			/* Clear the interrupt. */
+			out_le32(&(sata_dma_regs->interrupt_clear.error.low),
+				 DMA_CHANNEL(chan));
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/*******************************************************************************
+ * Function: dma_request_interrupts
+ * arguments: hsdev
+ * returns status
+ * This function registers ISR for a particular DMA channel interrupt
+ ******************************************************************************/
+static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq)
+{
+	int retval = 0;
+	int chan;
+
+	for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+		/* Unmask error interrupt */
+		out_le32(&sata_dma_regs->interrupt_mask.error.low,
+			 DMA_ENABLE_CHAN(chan));
+
+		/* Unmask end-of-transfer interrupt */
+		out_le32(&sata_dma_regs->interrupt_mask.tfr.low,
+			 DMA_ENABLE_CHAN(chan));
+	}
+
+	retval = request_irq(irq, dma_dwc_interrupt, 0, "SATA DMA", hsdev);
+	if (retval) {
+		dev_err(dwc_dev, "%s: could not get IRQ %d\n", __func__, irq);
+		return -ENODEV;
+	}
+
+	/* Mark this interrupt as requested */
+	hsdev->irq_dma = irq;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function: map_sg_to_lli
+ * arguments: sg: scatter/gather list(sg)
+ *	      num_elems: no of elements in sg list
+ *	      dma_lli: LLI table
+ *	      dest: destination address
+ *	      read: whether the transfer is read or write
+ * returns array of AHB DMA Linked List Items
+ * This function creates a list of LLIs for DMA Xfr and returns the number
+ * of elements in the DMA linked list.
+ *
+ * Note that the Synopsis driver has a comment proposing that better performance
+ * is possible by only enabling interrupts on the last item in the linked list.
+ * However, it seems that could be a problem if an error happened on one of the
+ * first items.  The transfer would halt, but no error interrupt would occur.
+ *
+ * Currently this function sets interrupts enabled for each linked list item:
+ * DMA_CTL_INT_EN.
+ ******************************************************************************/
+static int map_sg_to_lli(struct scatterlist *sg, int num_elems, struct lli *lli,
+			 dma_addr_t dma_lli, void __iomem *dmadr_addr, int dir)
+{
+	int i, idx = 0;
+	int fis_len = 0;
+	dma_addr_t next_llp;
+	int bl;
+	unsigned int dma_ts = 0;
+
+	dev_dbg(dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x "
+		"dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli,
+		(u32)dmadr_addr);
+
+	bl = get_burst_length_encode(AHB_DMA_BRST_DFLT);
+
+	for (i = 0; i < num_elems; i++, sg++) {
+		u32 addr, offset;
+		u32 sg_len, len;
+
+		addr = (u32) sg_dma_address(sg);
+		sg_len = sg_dma_len(sg);
+
+		dev_dbg(dwc_dev, "%s: elem=%d sg_addr=0x%x sg_len=%d\n",
+			__func__, i, addr, sg_len);
+
+		while (sg_len) {
+
+			if (idx >= SATA_DWC_DMAC_LLI_NUM) {
+				/* The LLI table is not large enough. */
+				dev_err(dwc_dev, "LLI table overrun (idx=%d)\n",
+						idx);
+				break;
+			}
+			len = (sg_len > SATA_DWC_DMAC_CTRL_TSIZE_MAX) ?
+				SATA_DWC_DMAC_CTRL_TSIZE_MAX : sg_len;
+
+			offset = addr & 0xffff;
+			if ((offset + sg_len) > 0x10000)
+				len = 0x10000 - offset;
+
+			/*
+			 * Make sure a LLI block is not created that will span a
+			 * 8K max FIS boundary.  If the block spans such a FIS
+			 * boundary, there is a chance that a DMA burst will
+			 * cross that boundary -- this results in an error in
+			 * the host controller.
+			 */
+			if (fis_len + len > 8192) {
+				dev_dbg(dwc_dev, "SPLITTING: fis_len=%d(0x%x) "
+					"len=%d(0x%x)\n", fis_len, fis_len,
+					len, len);
+				len = 8192 - fis_len;
+				fis_len = 0;
+			} else {
+				fis_len += len;
+			}
+			if (fis_len == 8192)
+				fis_len = 0;
+
+			/*
+			 * Set DMA addresses and lower half of control register
+			 * based on direction.
+			 */
+			if (dir == DMA_FROM_DEVICE) {
+				lli[idx].dar = cpu_to_le32(addr);
+				lli[idx].sar = cpu_to_le32((u32)dmadr_addr);
+
+				lli[idx].ctl.low = cpu_to_le32(
+					DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) |
+					DMA_CTL_SMS(0) |
+					DMA_CTL_DMS(1) |
+					DMA_CTL_SRC_MSIZE(bl) |
+					DMA_CTL_DST_MSIZE(bl) |
+					DMA_CTL_SINC_NOCHANGE |
+					DMA_CTL_SRC_TRWID(2) |
+					DMA_CTL_DST_TRWID(2) |
+					DMA_CTL_INT_EN |
+					DMA_CTL_LLP_SRCEN |
+					DMA_CTL_LLP_DSTEN);
+			} else {	/* DMA_TO_DEVICE */
+				lli[idx].sar = cpu_to_le32(addr);
+				lli[idx].dar = cpu_to_le32((u32)dmadr_addr);
+
+				lli[idx].ctl.low = cpu_to_le32(
+					DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) |
+					DMA_CTL_SMS(1) |
+					DMA_CTL_DMS(0) |
+					DMA_CTL_SRC_MSIZE(bl) |
+					DMA_CTL_DST_MSIZE(bl) |
+					DMA_CTL_DINC_NOCHANGE |
+					DMA_CTL_SRC_TRWID(2) |
+					DMA_CTL_DST_TRWID(2) |
+					DMA_CTL_INT_EN |
+					DMA_CTL_LLP_SRCEN |
+					DMA_CTL_LLP_DSTEN);
+			}
+
+			dev_dbg(dwc_dev, "%s setting ctl.high len: 0x%08x val: "
+					"0x%08x\n", __func__, len,
+					DMA_CTL_BLK_TS(len / 4));
+
+			/* Program the LLI CTL high register */
+			dma_ts = DMA_CTL_BLK_TS(len / 4);
+			lli[idx].ctl.high = cpu_to_le32(dma_ts);
+
+			/* Program the next pointer.  The next pointer must be
+			 * the physical address, not the virtual address.
+			 */
+			next_llp = (dma_lli + ((idx + 1) * sizeof(struct lli)));
+
+			/* The last 2 bits encode the list master select. */
+			next_llp = DMA_LLP_LMS(next_llp, DMA_LLP_AHBMASTER2);
+
+			lli[idx].llp = cpu_to_le32(next_llp);
+
+			idx++;
+			sg_len -= len;
+			addr += len;
+		}
+	}
+
+	/*
+	 * The last next ptr has to be zero and the last control low register
+	 * has to have LLP_SRC_EN and LLP_DST_EN (linked list pointer source
+	 * and destination enable) set back to 0 (disabled.)  This is what tells
+	 * the core that this is the last item in the linked list.
+	 */
+	if (idx) {
+		lli[idx-1].llp = 0x00000000;
+		lli[idx-1].ctl.low &= DMA_CTL_LLP_DISABLE_LE32;
+
+		/* Flush cache to memory */
+		dma_cache_sync(NULL, lli, (sizeof(struct lli) * idx),
+			       DMA_BIDIRECTIONAL);
+	}
+
+	return idx;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_xfer_start
+ * arguments: Channel number
+ * Return : None
+ * Enables the DMA channel
+ ******************************************************************************/
+static void dma_dwc_xfer_start(int dma_ch)
+{
+	/* Enable the DMA channel */
+	out_le32(&(sata_dma_regs->dma_chan_en.low),
+		 in_le32(&(sata_dma_regs->dma_chan_en.low)) |
+		 DMA_ENABLE_CHAN(dma_ch));
+}
+
+static int dma_dwc_channel_enabled(int ch)
+{
+	if (in_le32(&(sata_dma_regs->dma_chan_en.low)) & DMA_CHANNEL(ch))
+		return 1;
+
+	return 0;
+}
+
+static void dma_dwc_terminate_dma(int dma_ch)
+{
+	int enabled = dma_dwc_channel_enabled(dma_ch);
+
+	dev_info(dwc_dev, "%s terminate DMA on channel=%d enabled=%d\n",
+		 __func__, dma_ch, enabled);
+
+	if (enabled)  {
+		out_le32(&(sata_dma_regs->dma_chan_en.low),
+			 DMA_DISABLE_CHAN(dma_ch));
+
+		do {
+			enabled = dma_dwc_channel_enabled(dma_ch);
+		} while (enabled);
+	}
+}
+
+static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
+			      struct lli *lli, dma_addr_t dma_lli,
+			      void __iomem *addr, int dir)
+{
+	int dma_ch;
+	int num_lli;
+
+	/* Acquire DMA channel */
+	dma_ch = dma_request_channel();
+	if (dma_ch == -1) {
+		dev_err(dwc_dev, "%s: dma channel unavailable\n", __func__);
+		return -EAGAIN;
+	}
+
+	/* Convert SG list to linked list of items (LLIs) for AHB DMA */
+	num_lli = map_sg_to_lli(sg, num_elems, lli, dma_lli, addr, dir);
+
+	dev_dbg(dwc_dev, "%s sg: 0x%p, count: %d lli: %p dma_lli: 0x%0xlx addr:"
+		" %p lli count: %d\n", __func__, sg, num_elems, lli,
+		(u32)dma_lli, addr, num_lli);
+
+	/* Clear channel interrupts */
+	clear_chan_interrupts(dma_ch);
+
+	/* Program the CFG register. */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high),
+		 DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ);
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0);
+
+	/* Program the address of the linked list */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low),
+		 DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER2));
+
+	/* Program the CTL register with src enable / dst enable */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low),
+		 DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_exit
+ * arguments: None
+ * returns status
+ * This function exits the SATA DMA driver
+ ******************************************************************************/
+static void dma_dwc_exit(struct sata_dwc_device *hsdev)
+{
+	dev_dbg(dwc_dev, "%s:\n", __func__);
+	if (sata_dma_regs)
+		iounmap(sata_dma_regs);
+
+	if (hsdev->irq_dma)
+		free_irq(hsdev->irq_dma, hsdev);
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_init
+ * arguments: hsdev
+ * returns status
+ * This function initializes the SATA DMA driver
+ ******************************************************************************/
+static int dma_dwc_init(struct sata_dwc_device *hsdev, struct resource *res,
+			int irq)
+{
+	int err;
+
+	sata_dma_regs = ioremap(res->start, res->end - res->start + 1);
+	if (!sata_dma_regs) {
+		dev_err(dwc_dev, "%s: ioremap failed\n", __func__);
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	err = dma_request_interrupts(hsdev, irq);
+	if (err) {
+		dev_err(dwc_dev, "%s: dma_request_interrupts returns %d\n",
+			__func__, err);
+		goto error_out;
+	}
+
+	/* Enabe DMA */
+	out_le32(&(sata_dma_regs->dma_cfg.low), DMA_EN);
+
+	dev_notice(dwc_dev, "DMA initialized\n");
+	dev_dbg(dwc_dev, "SATA DMA registers=0x%p\n", sata_dma_regs);
+
+	return 0;
+
+error_out:
+	dma_dwc_exit(hsdev);
+
+	return err;
+}
+
+static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
+{
+	if (scr > SCR_NOTIFICATION) {
+		dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+				__func__, scr);
+		return -EINVAL;
+	}
+
+	*val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4));
+	dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n",
+		__func__, link->ap->print_id, scr, *val);
+
+	return 0;
+}
+
+static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val)
+{
+	dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n",
+		__func__, link->ap->print_id, scr, val);
+	if (scr > SCR_NOTIFICATION) {
+		dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+				__func__, scr);
+		return -EINVAL;
+	}
+	out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val);
+
+	return 0;
+}
+
+static inline u32 core_scr_read(unsigned int scr)
+{
+	return in_le32((void __iomem *)scr_addr_sstatus + (scr * 4));
+}
+
+static inline void core_scr_write(unsigned int scr, u32 val)
+{
+	out_le32((void __iomem *)scr_addr_sstatus + (scr * 4), val);
+}
+
+static inline void clear_serror(void)
+{
+	out_le32((void __iomem *)scr_addr_sstatus + 4,
+		 in_le32((void __iomem *)scr_addr_sstatus + 4));
+}
+
+static inline void clear_intpr(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intpr,
+		 in_le32(&hsdev->sata_dwc_regs->intpr));
+}
+
+static inline void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit)
+{
+	out_le32(&hsdev->sata_dwc_regs->intpr,
+		 in_le32(&hsdev->sata_dwc_regs->intpr));
+}
+
+static inline void disable_err_irq(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 in_le32(&hsdev->sata_dwc_regs->intmr) & ~SATA_DWC_INTMR_ERRM);
+	out_le32(&hsdev->sata_dwc_regs->errmr, ~SATA_DWC_SERROR_ERR_BITS);
+}
+
+static inline void enable_err_irq(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 in_le32(&hsdev->sata_dwc_regs->intmr) | SATA_DWC_INTMR_ERRM);
+	out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS);
+}
+
+static inline u32 qcmd_tag_to_mask(u8 tag)
+{
+	return 0x00000001 << (tag & 0x1f);
+}
+
+/* See ahci.c */
+static void sata_dwc_error_intr(struct ata_port *ap,
+				struct sata_dwc_device *hsdev, uint intpr)
+{
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	struct ata_eh_info *ehi = &ap->link.eh_info;
+	unsigned int err_mask = 0, action = 0;
+	struct ata_queued_cmd *qc;
+	u32 serror;
+	u8 status, tag;
+	u32 err_reg;
+
+	ata_ehi_clear_desc(ehi);
+
+	serror = core_scr_read(SCR_ERROR);
+	status = ap->ops->sff_check_status(ap);
+
+	err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+	tag = ap->link.active_tag;
+
+	dev_err(ap->dev, "%s SCR_ERROR=0x%08x intpr=0x%08x status=0x%08x "
+		"dma_intp=%d pending=%d issued=%d dma_err_status=0x%08x\n",
+		__func__, serror, intpr, status, dma_interrupt_count,
+		hsdevp->dma_pending[tag], hsdevp->cmd_issued[tag], err_reg);
+
+	/* Clear error register and interrupt bit */
+	clear_serror();
+	clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR);
+
+	/* This is the only error happening now. */
+	/* TODO check for exact error */
+	err_mask |= AC_ERR_HOST_BUS;
+	action |= ATA_EH_RESET;
+
+	/* Pass this on to EH */
+	ehi->serror |= serror;
+	ehi->action |= action;
+
+	qc = ata_qc_from_tag(ap, tag);
+	if (qc)
+		qc->err_mask |= err_mask;
+	else
+		ehi->err_mask |= err_mask;
+
+	ata_port_abort(ap);
+
+	/*
+	  if (irq_stat & PORT_IRQ_FREEZE)
+	  ata_port_freeze(ap);
+	  else
+	  ata_port_abort(ap);
+	*/
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_isr
+ * arguments : irq, void *dev_instance, struct pt_regs *regs
+ * Return value : irqreturn_t - status of IRQ
+ * This Interrupt handler called via port ops registered function.
+ * .irq_handler = sata_dwc_isr
+ ******************************************************************************/
+static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
+{
+	struct ata_host *host = (struct ata_host *)dev_instance;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
+	struct ata_port *ap;
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	u8 status, tag;
+	int handled, num_processed, port = 0;
+	uint intpr, sactive, sactive2, tag_mask;
+	struct sata_dwc_device_port *hsdevp;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Read the interrupt register */
+	intpr = in_le32(&hsdev->sata_dwc_regs->intpr);
+
+	ap = host->ports[port];
+	hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
+		ap->link.active_tag);
+
+	/* Check for error interrupt */
+	if (intpr & SATA_DWC_INTPR_ERR) {
+		sata_dwc_error_intr(ap, hsdev, intpr);
+		handled = 1;
+		goto DONE;
+	}
+
+	/* Check for DMA SETUP FIS (FP DMA) interrupt */
+	if (intpr & SATA_DWC_INTPR_NEWFP) {
+		clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);
+
+		tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr));
+		dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
+		if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND)
+			dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);
+
+		sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag);
+
+		qc = ata_qc_from_tag(ap, tag);
+		/* Start FP DMA for NCQ command.  At this point the tag is the
+		 * active tag.  It is the tag that matches the command about to
+		 * be completed.
+		 */
+		qc->ap->link.active_tag = tag;
+		sata_dwc_bmdma_start_by_tag(qc, tag);
+
+		handled = 1;
+		goto DONE;
+	}
+
+	sactive = core_scr_read(SCR_ACTIVE);
+	tag_mask = (sata_dwc_sactive_issued | sactive) ^ sactive;
+
+	/* If no sactive issued and tag_mask is zero then this is not NCQ */
+	if (sata_dwc_sactive_issued == 0 && tag_mask == 0) {
+		tag = 0;
+		qc = ata_qc_from_tag(ap, tag);
+
+		/* DEV interrupt w/ no active qc? */
+		if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
+			dev_err(ap->dev, "%s intr with no active qc qc=%p\n",
+				__func__, qc);
+			ata_sff_check_status(ap);
+			handled = 1;
+			goto DONE;
+		}
+
+		status = ap->ops->sff_check_status(ap);
+
+		qc->ap->link.active_tag = tag;
+		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;
+
+		if (status & ATA_ERR) {
+			dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
+			sata_dwc_qc_complete(ap, qc, 1);
+			handled = 1;
+			goto DONE;
+		}
+
+		dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
+			__func__, prot_2_txt(qc->tf.protocol));
+DRVSTILLBUSY:
+		if (ata_is_dma(qc->tf.protocol)) {
+			int dma_flag = hsdevp->dma_pending[tag];
+			/* Each DMA transaction produces 2 interrupts.  The DMAC
+			 * transfer complete interrupt and the SATA controller
+			 * operation done interrupt. The command should be
+			 * completed only after both interrupts are seen.
+			 */
+			dma_interrupt_count++;
+			if (dma_flag == SATA_DWC_DMA_PEND_NONE) {
+				dev_err(ap->dev, "%s: DMA not pending "
+					"intpr=0x%08x status=0x%08x pend=%d\n",
+					__func__, intpr, status, dma_flag);
+			}
+
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+		} else {
+			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
+				goto DRVSTILLBUSY;
+		}
+
+		handled = 1;
+		goto DONE;
+	}
+
+	/*
+	 * This is a NCQ command.  At this point we need to figure out for which
+	 * tags we have gotten a completion interrupt.  One interrupt may serve
+	 * as completion for more than one operation when commands are queued
+	 * (NCQ).  We need to process each completed command.
+	 */
+
+PROCESS:  /* process completed commands */
+	sactive = core_scr_read(SCR_ACTIVE);
+	tag_mask = (sata_dwc_sactive_issued | sactive) ^ sactive;
+
+	if (sactive != 0 || sata_dwc_sactive_issued > 1 || tag_mask > 1) {
+		dev_dbg(ap->dev, "%s NCQ: sactive=0x%08x  sactive_issued=0x%08x"
+			" tag_mask=0x%08x\n", __func__, sactive,
+			sata_dwc_sactive_issued, tag_mask);
+	}
+
+	if ((tag_mask | sata_dwc_sactive_issued) != sata_dwc_sactive_issued) {
+		dev_warn(ap->dev, "Bad tag mask?  sactive=0x%08x "
+			 "sata_dwc_sactive_issued=0x%08x  tag_mask=0x%08x\n",
+			 sactive, sata_dwc_sactive_issued, tag_mask);
+	}
+
+	/* read just to clear ... not bad if currently still busy */
+	status = ap->ops->sff_check_status(ap);
+	dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);
+
+	tag = 0;
+	num_processed = 0;
+	while (tag_mask) {
+		num_processed++;
+		while (!(tag_mask & 0x00000001)) {
+			tag++;
+			tag_mask <<= 1;
+		}
+		tag_mask &= (~0x00000001);
+		qc = ata_qc_from_tag(ap, tag);
+
+		/* To be picked up by completion functions */
+		qc->ap->link.active_tag = tag;
+		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;
+
+		/* Let libata/scsi layers handle error */
+		if (status & ATA_ERR) {
+			dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n",
+					 __func__, status);
+
+			sata_dwc_qc_complete(ap, qc, 1);
+			handled = 1;
+			goto DONE;
+		}
+
+		/* Process completed command */
+		dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__,
+			prot_2_txt(qc->tf.protocol));
+		if (ata_is_dma(qc->tf.protocol)) {
+			dma_interrupt_count++;
+			if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_NONE)
+				dev_warn(ap->dev,
+					"%s: DMA not pending?\n", __func__);
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+		} else {
+			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
+				goto STILLBUSY;
+		}
+		continue;
+
+STILLBUSY:
+		ap->stats.idle_irq++;
+		dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n",
+				ap->print_id);
+	} /* while tag_mask */
+
+	/*
+	 * Check to see if any commands completed while we were processing our
+	 * initial set of completed commands (reading of status clears
+	 * interrupts, so we might miss a completed command interrupt if one
+	 * came in while we were processing:
+	 * we read status as part of processing a completed command).
+	 */
+	sactive2 = core_scr_read(SCR_ACTIVE);
+	if (sactive2 != sactive) {
+		dev_dbg(ap->dev, "More finished - sactive=0x%x sactive2=0x%x\n",
+			sactive, sactive2);
+		goto PROCESS;
+	}
+	handled = 1;
+
+DONE:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag)
+{
+	struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp);
+
+	if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_RX) {
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+			 SATA_DWC_DMACR_RX_CLEAR(
+				 in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+	} else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_TX) {
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+			 SATA_DWC_DMACR_TX_CLEAR(
+				 in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+	} else {
+		/* This should not happen, it indicates the driver is out of
+		 * sync.  If it does happen, clear dmacr anyway.
+		 */
+		dev_err(dwc_dev, "%s DMA protocol RX and TX DMA not pending "
+			"tag=0x%02x pending=%d dmacr: 0x%08x\n",
+			__func__, tag, hsdevp->dma_pending[tag],
+			in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+				SATA_DWC_DMACR_TXRXCH_CLEAR);
+	}
+}
+
+static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status)
+{
+	struct ata_queued_cmd *qc;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	u8 tag = 0;
+
+	tag = ap->link.active_tag;
+	qc = ata_qc_from_tag(ap, tag);
+
+#ifdef DEBUG_NCQ
+	if (tag > 0) {
+		dev_info(ap->dev, "%s tag=%u cmd=0x%02x dma dir=%s proto=%s "
+			 "dmacr=0x%08x\n", __func__, qc->tag, qc->tf.command,
+			 dir_2_txt(qc->dma_dir), prot_2_txt(qc->tf.protocol),
+			 in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+	}
+#endif
+
+	if (ata_is_dma(qc->tf.protocol)) {
+		if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_NONE) {
+			dev_err(ap->dev, "%s DMA protocol RX and TX DMA not "
+				"pending dmacr: 0x%08x\n", __func__,
+				in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+		}
+
+		hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_NONE;
+		sata_dwc_qc_complete(ap, qc, check_status);
+		ap->link.active_tag = ATA_TAG_POISON;
+	} else {
+		sata_dwc_qc_complete(ap, qc, check_status);
+	}
+}
+
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+				u32 check_status)
+{
+	u8 status = 0;
+	int i = 0;
+	u32 mask = 0x0;
+	u8 tag = qc->tag;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	u32 serror;
+
+	dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status);
+
+	if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_TX)
+		dev_err(ap->dev, "TX DMA PENDING\n");
+	else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_RX)
+		dev_err(ap->dev, "RX DMA PENDING\n");
+
+	if (check_status) {
+		i = 0;
+		do {
+			/* check main status, clearing INTRQ */
+			status = ap->ops->sff_check_status(ap);
+			if (status & ATA_BUSY) {
+				dev_dbg(ap->dev, "STATUS BUSY (0x%02x) [%d]\n",
+						status, i);
+			}
+			if (++i > 10)
+				break;
+		} while (status & ATA_BUSY);
+
+		status = ap->ops->sff_check_status(ap);
+		if (unlikely(status & ATA_BUSY))
+			dev_err(ap->dev, "QC complete cmd=0x%02x STATUS BUSY "
+				"(0x%02x) [%d]\n", qc->tf.command, status, i);
+		serror = core_scr_read(SCR_ERROR);
+		if (serror & SATA_DWC_SERROR_ERR_BITS)
+			dev_err(ap->dev, "****** SERROR=0x%08x ******\n",
+				serror);
+	}
+	dev_dbg(ap->dev, "QC complete cmd=0x%02x status=0x%02x ata%u: "
+		"protocol=%d\n", qc->tf.command, status, ap->print_id,
+		qc->tf.protocol);
+
+	/* clear active bit */
+	mask = (~(qcmd_tag_to_mask(tag)));
+	sata_dwc_sactive_queued = sata_dwc_sactive_queued & mask;
+	sata_dwc_sactive_issued = sata_dwc_sactive_issued & mask;
+
+	/* Complete taskfile transaction (does not read SCR registers) */
+	ata_qc_complete(qc);
+
+	return 0;
+}
+
+static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev)
+{
+	/* Enable selective interrupts by setting the interrupt mask register */
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 SATA_DWC_INTMR_ERRM |
+		 SATA_DWC_INTMR_NEWFPM |
+		 SATA_DWC_INTMR_PMABRTM |
+		 SATA_DWC_INTMR_DMATM);
+
+	/* Unmask the error bits that should trigger an error interrupt by
+	 * setting the error mask register.
+	 */
+	out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS);
+
+	dev_dbg(dwc_dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", __func__,
+		in_le32(&hsdev->sata_dwc_regs->intmr),
+		in_le32(&hsdev->sata_dwc_regs->errmr));
+}
+
+static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base)
+{
+	port->cmd_addr = (void *)base + 0x00;
+	port->data_addr = (void *)base + 0x00;
+
+	port->error_addr = (void *)base + 0x04;
+	port->feature_addr = (void *)base + 0x04;
+
+	port->nsect_addr = (void *)base + 0x08;
+
+	port->lbal_addr = (void *)base + 0x0c;
+	port->lbam_addr = (void *)base + 0x10;
+	port->lbah_addr = (void *)base + 0x14;
+
+	port->device_addr = (void *)base + 0x18;
+	port->command_addr = (void *)base + 0x1c;
+	port->status_addr = (void *)base + 0x1c;
+
+	port->altstatus_addr = (void *)base + 0x20;
+	port->ctl_addr = (void *)base + 0x20;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_port_start
+ * arguments : struct ata_ioports *port
+ * Return value : returns 0 if success, error code otherwise
+ * This function allocates the scatter gather LLI table for AHB DMA
+ ******************************************************************************/
+static int sata_dwc_port_start(struct ata_port *ap)
+{
+	int err = 0;
+	struct sata_dwc_device *hsdev;
+	struct sata_dwc_device_port *hsdevp = NULL;
+	struct device *pdev;
+	u32 sstatus;
+	int i;
+
+	hsdev = HSDEV_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s: port_no=%d\n", __func__, ap->port_no);
+
+	hsdev->host = ap->host;
+	pdev = ap->host->dev;
+	if (!pdev) {
+		dev_err(ap->dev, "%s: no ap->host->dev\n", __func__);
+		err = -ENODEV;
+		goto CLEANUP;
+	}
+
+	/* Allocate Port Struct */
+	hsdevp = kmalloc(sizeof(*hsdevp), GFP_KERNEL);
+	if (!hsdevp) {
+		dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__);
+		err = -ENOMEM;
+		goto CLEANUP;
+	}
+	memset(hsdevp, 0, sizeof(*hsdevp));
+	hsdevp->hsdev = hsdev;
+
+	for (i = 0; i < SATA_DWC_QCMD_MAX; i++)
+		hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT;
+
+	ap->prd = 0;	/* set these so libata doesn't use them */
+	ap->prd_dma = 0;
+
+	/*
+	 * DMA - Assign scatter gather LLI table. We can't use the libata
+	 * version since it's PRD is IDE PCI specific.
+	 */
+	for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+		hsdevp->llit[i] = dma_alloc_coherent(pdev,
+						     SATA_DWC_DMAC_LLI_TBL_SZ,
+						     &(hsdevp->llit_dma[i]),
+						     GFP_ATOMIC);
+		if (!hsdevp->llit[i]) {
+			dev_err(ap->dev, "%s: dma_alloc_coherent failed size "
+				"0x%x\n", __func__, SATA_DWC_DMAC_LLI_TBL_SZ);
+			err = -ENOMEM;
+			goto CLEANUP;
+		}
+	}
+
+	if (ap->port_no == 0)  {
+		dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n",
+				__func__);
+
+		out_le32(&hsdev->sata_dwc_regs->dmacr,
+			 SATA_DWC_DMACR_TXRXCH_CLEAR);
+
+		dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", __func__);
+		out_le32(&hsdev->sata_dwc_regs->dbtsr,
+			 (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) |
+			  SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)));
+	}
+
+	/* Clear any error bits before libata starts issuing commands */
+	clear_serror();
+
+	ap->private_data = hsdevp;
+
+	/* Are we in Gen I or II */
+	sstatus = core_scr_read(SCR_STATUS);
+	switch (SATA_DWC_SCR0_SPD_GET(sstatus)) {
+	case 0x0:
+		dev_info(ap->dev, "**** No neg speed (nothing attached?) \n");
+		break;
+	case 0x1:
+		dev_info(ap->dev, "**** GEN I speed rate negotiated \n");
+		break;
+	case 0x2:
+		dev_info(ap->dev, "**** GEN II speed rate negotiated \n");
+		break;
+	}
+
+CLEANUP:
+	if (err) {
+		kfree(hsdevp);
+		sata_dwc_port_stop(ap);
+		dev_dbg(ap->dev, "%s: fail\n", __func__);
+	} else {
+		dev_dbg(ap->dev, "%s: done\n", __func__);
+	}
+
+	return err;
+}
+
+static void sata_dwc_port_stop(struct ata_port *ap)
+{
+	int i;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id);
+
+	if (hsdevp && hsdev) {
+		/* deallocate LLI table */
+		for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+			dma_free_coherent(ap->host->dev,
+					  SATA_DWC_DMAC_LLI_TBL_SZ,
+					  hsdevp->llit[i], hsdevp->llit_dma[i]);
+		}
+
+		kfree(hsdevp);
+	}
+	ap->private_data = NULL;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_exec_command_by_tag
+ * arguments : ata_port *ap, ata_taskfile *tf, u8 tag, u32 cmd_issued
+ * Return value : None
+ * This function keeps track of individual command tag ids and calls
+ * ata_exec_command in libata
+ ******************************************************************************/
+static void sata_dwc_exec_command_by_tag(struct ata_port *ap,
+					 struct ata_taskfile *tf,
+					 u8 tag, u32 cmd_issued)
+{
+	unsigned long flags;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command,
+		ata_cmd_2_txt(tf), tag);
+
+	spin_lock_irqsave(&ap->host->lock, flags);
+	hsdevp->cmd_issued[tag] = cmd_issued;
+	spin_unlock_irqrestore(&ap->host->lock, flags);
+
+	/* Clear SError before executing a new command.
+	 *
+	 * TODO if we read a PM's registers now, we will throw away the task
+	 * file values loaded into the shadow registers for this command.
+	 *
+	 * sata_dwc_scr_write and read can not be used here. Clearing the PM
+	 * managed SError register for the disk needs to be done before the
+	 * task file is loaded.
+	 */
+	clear_serror();
+	ata_sff_exec_command(ap, tf);
+}
+
+static void sata_dwc_bmdma_setup_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	sata_dwc_exec_command_by_tag(qc->ap, &qc->tf, tag,
+				     SATA_DWC_CMD_ISSUED_PEND);
+}
+
+static void sata_dwc_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	u8 tag = qc->tag;
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+			__func__, qc->ap->link.sactive, tag);
+	} else {
+		tag = 0;
+	}
+
+	sata_dwc_bmdma_setup_by_tag(qc, tag);
+}
+
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	int start_dma;
+	u32 reg, dma_chan;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_QC(qc);
+	struct ata_port *ap = qc->ap;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	int dir = qc->dma_dir;
+	dma_chan = hsdevp->dma_chan[tag];
+
+	/* Used for ata_bmdma_start(qc) -- we are not BMDMA compatible */
+
+	if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_NOT) {
+		start_dma = 1;
+		if (dir == DMA_TO_DEVICE)
+			hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_TX;
+		else
+			hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_RX;
+	} else {
+		dev_err(ap->dev, "%s: Command not pending cmd_issued=%d "
+			"(tag=%d) - DMA NOT started\n", __func__,
+			hsdevp->cmd_issued[tag], tag);
+		start_dma = 0;
+	}
+
+	dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s "
+			"start_dma? %x\n", __func__, qc, tag, qc->tf.command,
+			dir_2_txt(qc->dma_dir), start_dma);
+	sata_dwc_tf_dump(&(qc->tf));
+
+	if (start_dma) {
+		reg = core_scr_read(SCR_ERROR);
+		if (reg & SATA_DWC_SERROR_ERR_BITS) {
+			dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n",
+				__func__, reg);
+		}
+
+		if (dir == DMA_TO_DEVICE)
+			out_le32(&hsdev->sata_dwc_regs->dmacr,
+					SATA_DWC_DMACR_TXCHEN);
+		else
+			out_le32(&hsdev->sata_dwc_regs->dmacr,
+					SATA_DWC_DMACR_RXCHEN);
+
+		/* Enable AHB DMA transfer on the specified channel */
+		dma_dwc_xfer_start(dma_chan);
+	}
+}
+
+static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc)
+{
+	u8 tag = qc->tag;
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+			__func__, qc->ap->link.sactive, tag);
+	} else {
+		tag = 0;
+	}
+
+	dev_dbg(qc->ap->dev, "%s\n", __func__);
+	sata_dwc_bmdma_start_by_tag(qc, tag);
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_qc_prep_by_tag
+ * arguments : ata_queued_cmd *qc, u8 tag
+ * Return value : None
+ * qc_prep for a particular queued command based on tag
+ ******************************************************************************/
+static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	struct scatterlist *sg = qc->sg;
+	struct ata_port *ap = qc->ap;
+	u32 dma_chan;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	int dir;
+	int err;
+
+	dir = qc->dma_dir;
+
+	if ((dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO))
+		return;
+
+	dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n",
+		__func__, ap->port_no, dir_2_txt(dir), qc->n_elem);
+
+	dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag],
+			hsdevp->llit_dma[tag],
+			(void *__iomem)(&hsdev->sata_dwc_regs->dmadr),
+			dir);
+	if (dma_chan < 0) {
+		dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n",
+			__func__, err);
+		return;
+	}
+
+	hsdevp->dma_chan[tag] = dma_chan;
+}
+
+static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+#ifdef DEBUG_NCQ
+	if (qc->tag > 0 || ap->link.sactive > 1) {
+		dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d prot=%s"
+			" ap active_tag=0x%08x ap sactive=0x%08x\n",
+			__func__, ap->print_id, qc->tf.command,
+			ata_cmd_2_txt(&qc->tf), qc->tag,
+			prot_2_txt(qc->tf.protocol), ap->link.active_tag,
+			ap->link.sactive);
+	}
+#endif
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		ap->ops->sff_tf_load(ap, &qc->tf);
+		sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag,
+					     SATA_DWC_CMD_ISSUED_PEND);
+	} else {
+		ata_sff_qc_issue(qc);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_eng_timeout
+ * arguments : ata_port *ap
+ * Return value : None
+ * error handler for DMA time out
+ * ata_eng_timeout(ap) -- this does bmdma stuff which can not be done by this
+ * driver. SEE ALSO ata_qc_timeout(ap)
+ ******************************************************************************/
+static void sata_dwc_eng_timeout(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	u8  tag;
+	uint mask = 0x0;
+	unsigned long flags;
+	u32 serror, intpr, dma_ch;
+
+	tag = ap->link.active_tag;
+	dma_ch = hsdevp->dma_chan[tag];
+	qc = ata_qc_from_tag(ap, tag);
+
+	dev_err(ap->dev, "%s: id=%d active_tag=%d qc=%p dma_chan=%d\n",
+		__func__, ap->print_id, tag, qc, dma_ch);
+
+	if (!qc) {
+		dev_err(ap->dev,
+			"%s: timeout without queued command\n", __func__);
+		return;
+	}
+
+	intpr = in_le32(&hsdev->sata_dwc_regs->intpr);
+	serror = core_scr_read(SCR_ERROR);
+
+	dev_err(ap->dev, "intpr=0x%08x serror=0x%08x\n", intpr, serror);
+
+	/* If there are no error bits set, can we just pass this on to eh? */
+	if (!(serror & SATA_DWC_SERROR_ERR_BITS) &&
+	    !(intpr & SATA_DWC_INTPR_ERR)) {
+
+		spin_lock_irqsave(&ap->host->lock, flags);
+		if (dma_dwc_channel_enabled(dma_ch))
+			dma_dwc_terminate_dma(dma_ch);
+
+		hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_NONE;
+
+		/* clear active bit */
+		mask = (~(qcmd_tag_to_mask(tag)));
+		sata_dwc_sactive_queued = sata_dwc_sactive_queued & mask;
+		sata_dwc_sactive_issued = sata_dwc_sactive_issued & mask;
+
+		spin_unlock_irqrestore(&ap->host->lock, flags);
+	} else {
+		/* This is wrong, what really needs to be done is a reset. */
+
+		spin_lock_irqsave(ap->lock, flags);
+
+		if (ata_is_dma(qc->tf.protocol)) {
+			/* disable DMAC */
+			dma_dwc_terminate_dma(dma_ch);
+		}
+
+		spin_unlock_irqrestore(ap->lock, flags);
+	}
+
+	WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE));
+	if (qc->flags & ATA_QCFLAG_ACTIVE) {
+		qc->err_mask |= AC_ERR_TIMEOUT;
+		/*
+		 * test-only: The original code (AMCC: 2.6.19) called
+		 * ata_eng_timeout(ap) here. This function is not available
+		 * anymore. So what to do now?
+		 */
+	}
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_qc_prep
+ * arguments : ata_queued_cmd *qc
+ * Return value : None
+ * qc_prep for a particular queued command
+ ******************************************************************************/
+static void sata_dwc_qc_prep(struct ata_queued_cmd *qc)
+{
+	u32 sactive;
+	u8 tag = qc->tag;
+
+#ifdef DEBUG_NCQ
+	if (qc->tag > 0) {
+		dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n",
+			 __func__, tag, qc->ap->link.active_tag);
+	}
+#endif
+
+	if (qc->tf.protocol == ATA_PROT_NCQ) {
+		sactive = core_scr_read(SCR_ACTIVE);
+		sactive |= (0x00000001 << tag);
+		core_scr_write(SCR_ACTIVE, sactive);
+		dev_dbg(qc->ap->dev, "%s: tag=%d ap->link.sactive = 0x%08x "
+			"sactive=0x%08x\n", __func__, tag, qc->ap->link.sactive,
+			sactive);
+	} else {
+		tag = 0;
+	}
+
+	sata_dwc_qc_prep_by_tag(qc, tag);
+}
+
+/*
+ * test-only: Needed when no drive is connected upon driver startup.
+ * Otherwise, by using the default prereset routine, the driver crashes
+ * upon loading.
+ * Not sure if this could be handled differently.
+ */
+static int sata_dwc_prereset(struct ata_link *link, unsigned long deadline)
+{
+	struct ata_port *ap = link->ap;
+	struct ata_eh_context *ehc = &ap->link.eh_context;
+	int rc = 0;
+
+	if (ata_link_online(link)) {
+		rc = ata_sff_wait_ready(link, deadline);
+	} else {
+		/* tell EH to bail */
+		ehc->i.action &= ~ATA_EH_RESET;
+	}
+
+	return rc;
+}
+
+/*
+ * scsi mid-layer and libata interface structures
+ */
+static struct scsi_host_template sata_dwc_sht = {
+	ATA_NCQ_SHT(DRV_NAME),
+	/*
+	 * test-only: Currently this driver doesn't handle NCQ
+	 * correctly. We enable NCQ but set the queue depth to a
+	 * max of 1. This will get fixed in in a future release.
+	 */
+	.can_queue = ATA_DEF_QUEUE,	/* ATA_MAX_QUEUE */
+	.dma_boundary = ATA_DMA_BOUNDARY,
+};
+
+static struct ata_port_operations sata_dwc_ops = {
+	.inherits		= &ata_sff_port_ops,
+
+	.qc_prep		= sata_dwc_qc_prep,
+	.qc_issue		= sata_dwc_qc_issue,
+
+	.scr_read		= sata_dwc_scr_read,
+	.scr_write		= sata_dwc_scr_write,
+
+	.port_start		= sata_dwc_port_start,
+	.port_stop		= sata_dwc_port_stop,
+
+	.bmdma_setup		= sata_dwc_bmdma_setup,
+	.bmdma_start		= sata_dwc_bmdma_start,
+
+	.prereset		= sata_dwc_prereset,
+
+	/* test-only: really needed? */
+	.eng_timeout		= sata_dwc_eng_timeout,
+};
+
+static const struct ata_port_info sata_dwc_port_info[] = {
+	{
+		/*
+		 * test-only: Currently this driver doesn't handle NCQ
+		 * correctly. So we disable NCQ here for now. To enable
+		 * it ATA_FLAG_NCQ needs to be added to the flags below.
+		 */
+		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_MMIO,
+		.pio_mask	= 0x1f,	/* pio 0-4 */
+		.udma_mask	= ATA_UDMA6,
+		.port_ops	= &sata_dwc_ops,
+	},
+};
+
+static int sata_dwc_probe(struct platform_device *pdev)
+{
+	struct sata_dwc_device *hsdev;
+	u32 idr, versionr;
+	char *ver = (char *)&versionr;
+	u8 *base = NULL;
+	int err = 0;
+	struct resource *res;
+	int irq;
+	struct ata_host *host;
+	struct ata_port_info pi = sata_dwc_port_info[0];
+	const struct ata_port_info *ppi[] = { &pi, NULL };
+
+
+	/* Allocate DWC SATA device */
+	hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
+	if (hsdev == NULL) {
+		dev_err(&pdev->dev, "kmalloc failed for hsdev\n");
+		err = -ENOMEM;
+		goto error_out;
+	}
+	memset(hsdev, 0, sizeof(*hsdev));
+
+	/* Get physical SATA register base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no reg addr\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	/* Ioremap SATA registers */
+	base = ioremap(res->start, res->end - res->start + 1);
+	if (!base) {
+		dev_err(&pdev->dev, "ioremap failed for 0x%llx\n", res->start);
+		err = -ENODEV;
+		goto error_out;
+	}
+	hsdev->reg_base = base;
+	dev_dbg(&pdev->dev, "ioremap done (0x%llx -> 0x%08x)\n",
+			res->start, (u32)base);
+
+	/* Synopsys DWC SATA specific Registers */
+	hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET);
+
+	/* Allocate and fill host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, SATA_DWC_MAX_PORTS);
+	if (!host) {
+		dev_err(&pdev->dev, "ata_host_alloc_pinfo failed\n");
+		err = -ENOMEM;
+		goto error_out;
+	}
+
+	host->private_data = hsdev;
+
+	/* Setup port */
+	host->ports[0]->ioaddr.cmd_addr = base;
+	host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET;
+	scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET;
+	sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base);
+
+	/* Read the ID and Version Registers */
+	idr = in_le32(&hsdev->sata_dwc_regs->idr);
+	versionr = in_le32(&hsdev->sata_dwc_regs->versionr);
+	dev_notice(&pdev->dev, "id %d, controller version %c.%c%c\n",
+		   idr, ver[0], ver[1], ver[2]);
+
+	/* Get SATA DMA interrupt number */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no SATA DMA irq\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+	irq = res->start;
+
+	/* Get physical SATA DMA register base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no reg addr\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	/* Save dev for later use in dev_xxx() routines */
+	dwc_dev = &pdev->dev;
+
+	/* Initialize AHB DMAC */
+	dma_dwc_init(hsdev, res, irq);
+
+	/* Enable SATA Interrupts */
+	sata_dwc_enable_interrupts(hsdev);
+
+	/* Get SATA interrupt number */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no SATA irq\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+	irq = res->start;
+
+	/*
+	 * Now, register with libATA core, this will also initiate the
+	 * device discovery process, invoking our port_start() handler &
+	 * error_handler() to execute a dummy Softreset EH session
+	 */
+	ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht);
+
+	dev_set_drvdata(&pdev->dev, host);
+
+	/* Everything is fine */
+	return 0;
+
+error_out:
+	/* Free SATA DMA resources */
+	dma_dwc_exit(hsdev);
+
+	if (base)
+		iounmap(base);
+
+	kfree(hsdev);
+
+	return err;
+}
+
+static int sata_dwc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct sata_dwc_device *hsdev = host->private_data;
+
+	ata_host_detach(host);
+
+	dev_set_drvdata(dev, NULL);
+
+	/* Free SATA DMA resources */
+	dma_dwc_exit(hsdev);
+
+	iounmap(hsdev->reg_base);
+	kfree(hsdev);
+	kfree(host);
+
+	dev_dbg(&pdev->dev, "done\n");
+
+	return 0;
+}
+
+static struct platform_driver sata_dwc_driver = {
+	.probe = sata_dwc_probe,
+	.remove = sata_dwc_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init sata_dwc_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&sata_dwc_driver);
+
+	return err;
+}
+
+static void __exit sata_dwc_exit(void)
+{
+	platform_driver_unregister(&sata_dwc_driver);
+}
+
+module_init(sata_dwc_init);
+module_exit(sata_dwc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>");
+MODULE_DESCRIPTION("DesignWare Cores SATA controller low lever driver");
+MODULE_VERSION(DRV_VERSION);
-- 
1.5.5

^ permalink raw reply related

* Re: [PATCH 3/6] net/ucc_geth: update riscTx and riscRx in ucc_geth
From: David Miller @ 2009-04-29 21:51 UTC (permalink / raw)
  To: galak; +Cc: linuxppc-dev, netdev, Haiying.Wang
In-Reply-To: <F640C411-797C-4A4D-BD1B-9A20114C57C3@kernel.crashing.org>

From: Kumar Gala <galak@kernel.crashing.org>
Date: Wed, 29 Apr 2009 15:23:21 -0500

> 
> On Apr 29, 2009, at 1:14 PM, Haiying Wang wrote:
> 
>> Change the definition of riscTx and riscRx to unsigned integer instead
>> of enum,
>> and change their values to support 4 risc allocation if the qe has 4
>> RISC
>> engines.
>>
>> Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com>
>> ---
> 
> Dave, if you can ack this it is probably easier if this goes via the
> powerpc tree as it had some dependancies.

Fair enough:

Acked-by: David S. Miller <davem@davemloft.net>

^ permalink raw reply

* Re: [PATCH 1/2] Add support for Designware SATA controller driver
From: Kumar Gala @ 2009-04-29 21:47 UTC (permalink / raw)
  To: Feng Kan; +Cc: linuxppc-dev, linuxppc-embedded
In-Reply-To: <1241041345-19479-1-git-send-email-fkan@amcc.com>


On Apr 29, 2009, at 4:42 PM, Feng Kan wrote:

> Signed-off-by: Feng Kan <fkan@amcc.com>
> ---
> drivers/ata/Kconfig    |   76 +-
> drivers/ata/Makefile   |    1 +
> drivers/ata/sata_dwc.c | 2047 +++++++++++++++++++++++++++++++++++++++ 
> +++++++++
> 3 files changed, 2091 insertions(+), 33 deletions(-)
> create mode 100644 drivers/ata/sata_dwc.c

you should copy Jeff & linux-ide@vger.kernel.org on SATA drivers.

- k

^ permalink raw reply

* [PATCH 2/2] Added AMCC 460EX Canyonlands SATA support.
From: Feng Kan @ 2009-04-29 21:42 UTC (permalink / raw)
  To: linuxppc-embedded, linuxppc-dev; +Cc: Feng Kan

Signed-off-by: Feng Kan <fkan@amcc.com>
---
 arch/powerpc/boot/dts/canyonlands.dts  |    8 ++
 arch/powerpc/platforms/44x/Makefile    |    4 +
 arch/powerpc/platforms/44x/amcc-sata.c |  125 ++++++++++++++++++++++++++++++++
 3 files changed, 137 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/44x/amcc-sata.c

diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 5fd1ad0..b536223 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -163,6 +163,14 @@
                         interrupts = <0x1e 4>;
                 };
 
+                SATA0: sata@bffd1000 {
+                        compatible = "amcc,sata-460ex";
+			reg = <4 0xbffd1000 0x800 4 0xbffd0800 0x400>;
+                        interrupt-parent = <&UIC3>;
+                        interrupts = <0 4       /* SATA */
+                                      5 4>;     /* AHBDMA */
+                };
+
 		POB0: opb {
 			compatible = "ibm,opb-460ex", "ibm,opb";
 			#address-cells = <1>;
diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile
index 01f51da..fa0a999 100644
--- a/arch/powerpc/platforms/44x/Makefile
+++ b/arch/powerpc/platforms/44x/Makefile
@@ -4,3 +4,7 @@ obj-$(CONFIG_EBONY)	+= ebony.o
 obj-$(CONFIG_SAM440EP) 	+= sam440ep.o
 obj-$(CONFIG_WARP)	+= warp.o
 obj-$(CONFIG_XILINX_VIRTEX_5_FXT) += virtex.o
+ifeq ($(CONFIG_SATA_DWC),y)
+obj-$(CONFIG_CANYONLANDS) += amcc-sata.o
+endif
+
diff --git a/arch/powerpc/platforms/44x/amcc-sata.c b/arch/powerpc/platforms/44x/amcc-sata.c
new file mode 100644
index 0000000..fdda917
--- /dev/null
+++ b/arch/powerpc/platforms/44x/amcc-sata.c
@@ -0,0 +1,125 @@
+/*
+ * AMCC Canyonlands SATA wrapper
+ *
+ * Copyright 2008 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * Extract the resources (MEM & IRQ) from the dts file and put them
+ * into the platform-device struct for usage in the platform-device
+ * SATA driver.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+
+/*
+ * Resource template will be filled dynamically with the values
+ * extracted from the dts file
+ */
+static struct resource sata_resources[] = {
+	[0] = {
+		/* 460EX SATA registers */
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		/* 460EX AHBDMA registers */
+		.flags  = IORESOURCE_MEM,
+	},
+	[2] = {
+		/* 460EX SATA IRQ */
+		.flags  = IORESOURCE_IRQ,
+	},
+	[3] = {
+		/* 460EX AHBDMA IRQ */
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static u64 dma_mask = 0xffffffffULL;
+
+static struct platform_device sata_device = {
+	.name = "sata-dwc",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(sata_resources),
+	.resource = sata_resources,
+	.dev = {
+		.dma_mask = &dma_mask,
+		.coherent_dma_mask = 0xffffffffULL,
+	}
+};
+
+static struct platform_device *ppc460ex_devs[] __initdata = {
+	&sata_device,
+};
+
+static int __devinit ppc460ex_sata_probe(struct of_device *ofdev,
+					 const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct resource res;
+	const char *val;
+
+	/*
+	 * Check if device is enabled
+	 */
+	val = of_get_property(np, "status", NULL);
+	if (val && !strcmp(val, "disabled")) {
+		printk(KERN_INFO "SATA port disabled via device-tree\n");
+		return 0;
+	}
+
+	/*
+	 * Extract register address reange from device tree and put it into
+	 * the platform device structure
+	 */
+	if (of_address_to_resource(np, 0, &res)) {
+		printk(KERN_ERR "%s: Can't get SATA register address\n",
+				__func__);
+		return -ENOMEM;
+	}
+	sata_resources[0].start = res.start;
+	sata_resources[0].end = res.end;
+
+	if (of_address_to_resource(np, 1, &res)) {
+		printk(KERN_ERR "%s: Can't get AHBDMA register address\n",
+				__func__);
+		return -ENOMEM;
+	}
+	sata_resources[1].start = res.start;
+	sata_resources[1].end = res.end;
+
+	/*
+	 * Extract IRQ number(s) from device tree and put them into
+	 * the platform device structure
+	 */
+	sata_resources[2].start = sata_resources[2].end =
+		irq_of_parse_and_map(np, 0);
+	sata_resources[3].start = sata_resources[3].end =
+		irq_of_parse_and_map(np, 1);
+
+	return platform_add_devices(ppc460ex_devs, ARRAY_SIZE(ppc460ex_devs));
+}
+
+static int __devexit ppc460ex_sata_remove(struct of_device *ofdev)
+{
+	/* Nothing to do here */
+	return 0;
+}
+
+static const struct of_device_id ppc460ex_sata_match[] = {
+	{ .compatible = "amcc,sata-460ex", },
+	{}
+};
+
+static struct of_platform_driver ppc460ex_sata_driver = {
+	.name = "sata-460ex",
+	.match_table = ppc460ex_sata_match,
+	.probe = ppc460ex_sata_probe,
+	.remove = ppc460ex_sata_remove,
+};
+
+static int __init ppc460ex_sata_init(void)
+{
+	return of_register_platform_driver(&ppc460ex_sata_driver);
+}
+device_initcall(ppc460ex_sata_init);
-- 
1.5.5

^ permalink raw reply related

* [PATCH 1/2] Add support for Designware SATA controller driver
From: Feng Kan @ 2009-04-29 21:42 UTC (permalink / raw)
  To: linuxppc-embedded, linuxppc-dev; +Cc: Feng Kan

Signed-off-by: Feng Kan <fkan@amcc.com>
---
 drivers/ata/Kconfig    |   76 +-
 drivers/ata/Makefile   |    1 +
 drivers/ata/sata_dwc.c | 2047 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2091 insertions(+), 33 deletions(-)
 create mode 100644 drivers/ata/sata_dwc.c

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 0bcf264..5321e47 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -72,56 +72,66 @@ config SATA_FSL
 
 	  If unsure, say N.
 
-config ATA_SFF
-	bool "ATA SFF support"
-	default y
+config SATA_DWC
+	tristate "DesignWare Cores SATA support"
+	depends on 460EX
 	help
-	  This option adds support for ATA controllers with SFF
-	  compliant or similar programming interface.
+	  This option enables support for the Synopsys DesignWare Cores SATA
+	  controller.
+	  It can be found on the AMCC 460EX.
 
-	  SFF is the legacy IDE interface that has been around since
-	  the dawn of time.  Almost all PATA controllers have an
-	  SFF interface.  Many SATA controllers have an SFF interface
-	  when configured into a legacy compatibility mode.
+	  If unsure, say N.
 
-	  For users with exclusively modern controllers like AHCI,
-	  Silicon Image 3124, or Marvell 6440, you may choose to
-	  disable this uneeded SFF support.
+config ATA_SFF
+bool "ATA SFF support"
+default y
+help
+  This option adds support for ATA controllers with SFF
+  compliant or similar programming interface.
 
-	  If unsure, say Y.
+  SFF is the legacy IDE interface that has been around since
+  the dawn of time.  Almost all PATA controllers have an
+  SFF interface.  Many SATA controllers have an SFF interface
+  when configured into a legacy compatibility mode.
+
+  For users with exclusively modern controllers like AHCI,
+  Silicon Image 3124, or Marvell 6440, you may choose to
+  disable this uneeded SFF support.
+
+  If unsure, say Y.
 
 if ATA_SFF
 
 config SATA_SVW
-	tristate "ServerWorks Frodo / Apple K2 SATA support"
-	depends on PCI
-	help
-	  This option enables support for Broadcom/Serverworks/Apple K2
-	  SATA support.
+tristate "ServerWorks Frodo / Apple K2 SATA support"
+depends on PCI
+help
+  This option enables support for Broadcom/Serverworks/Apple K2
+  SATA support.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config ATA_PIIX
-	tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
-	depends on PCI
-	help
-	  This option enables support for ICH5/6/7/8 Serial ATA
-	  and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
-	  host controllers.
+tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
+depends on PCI
+help
+  This option enables support for ICH5/6/7/8 Serial ATA
+  and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
+  host controllers.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config SATA_MV
-	tristate "Marvell SATA support"
-	help
-	  This option enables support for the Marvell Serial ATA family.
-	  Currently supports 88SX[56]0[48][01] PCI(-X) chips,
-	  as well as the newer [67]042 PCI-X/PCIe and SOC devices.
+tristate "Marvell SATA support"
+help
+  This option enables support for the Marvell Serial ATA family.
+  Currently supports 88SX[56]0[48][01] PCI(-X) chips,
+  as well as the newer [67]042 PCI-X/PCIe and SOC devices.
 
-	  If unsure, say N.
+  If unsure, say N.
 
 config SATA_NV
-	tristate "NVIDIA SATA support"
+tristate "NVIDIA SATA support"
 	depends on PCI
 	help
 	  This option enables support for NVIDIA Serial ATA.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 7f1ecf9..3d41fc7 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SATA_MV)		+= sata_mv.o
 obj-$(CONFIG_SATA_INIC162X)	+= sata_inic162x.o
 obj-$(CONFIG_PDC_ADMA)		+= pdc_adma.o
 obj-$(CONFIG_SATA_FSL)		+= sata_fsl.o
+obj-$(CONFIG_SATA_DWC)          += sata_dwc.o
 
 obj-$(CONFIG_PATA_ALI)		+= pata_ali.o
 obj-$(CONFIG_PATA_AMD)		+= pata_amd.o
diff --git a/drivers/ata/sata_dwc.c b/drivers/ata/sata_dwc.c
new file mode 100644
index 0000000..1a6fce0
--- /dev/null
+++ b/drivers/ata/sata_dwc.c
@@ -0,0 +1,2047 @@
+/*
+ * drivers/ata/sata_dwc.c
+ *
+ * Synopsys DesignWare Cores (DWC) SATA host driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@amcc.com>
+ *
+ * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de>
+ * Copyright 2008 DENX Software Engineering
+ *
+ * Based on versions provided by AMCC and Synopsys which are:
+ *          Copyright 2006 Applied Micro Circuits Corporation
+ *          COPYRIGHT (C) 2005  SYNOPSYS, INC.  ALL RIGHTS RESERVED
+ *
+ * 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.
+ *
+ */
+#ifdef CONFIG_SATA_DWC_DEBUG
+#define DEBUG
+#endif
+
+#ifdef CONFIG_SATA_DWC_VDEBUG
+#define VERBOSE_DEBUG
+#define DEBUG_NCQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/libata.h>
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+
+#define DRV_NAME        "sata-dwc"
+#define DRV_VERSION     "1.0"
+
+/* SATA DMA driver Globals */
+#define DMA_NUM_CHANS			1
+#define DMA_NUM_CHAN_REGS		8
+
+/* SATA DMA Register definitions */
+#define AHB_DMA_BRST_DFLT		64	/* 16 data items burst length */
+
+struct dmareg {
+	u32 low;		/* Low bits 0-31 */
+	u32 high;		/* High bits 32-63 */
+};
+
+/* DMA Per Channel registers */
+
+struct dma_chan_regs {
+	struct dmareg sar;	/* Source Address */
+	struct dmareg dar;	/* Destination address */
+	struct dmareg llp;	/* Linked List Pointer */
+	struct dmareg ctl;	/* Control */
+	struct dmareg sstat;	/* Source Status not implemented in core */
+	struct dmareg dstat;	/* Destination Status not implemented in core */
+	struct dmareg sstatar;	/* Source Status Address not impl in core */
+	struct dmareg dstatar;	/* Destination Status Address not implemented */
+	struct dmareg cfg;	/* Config */
+	struct dmareg sgr;	/* Source Gather */
+	struct dmareg dsr;	/* Destination Scatter */
+};
+
+/* Generic Interrupt Registers */
+struct dma_interrupt_regs {
+	struct dmareg tfr;	/* Transfer Interrupt */
+	struct dmareg block;	/* Block Interrupt */
+	struct dmareg srctran;	/* Source Transfer Interrupt */
+	struct dmareg dsttran;	/* Dest Transfer Interrupt */
+	struct dmareg error;	/* Error */
+};
+
+struct ahb_dma_regs {
+	struct dma_chan_regs	chan_regs[DMA_NUM_CHAN_REGS];
+	struct dma_interrupt_regs interrupt_raw;	/* Raw Interrupt */
+	struct dma_interrupt_regs interrupt_status;	/* Interrupt Status */
+	struct dma_interrupt_regs interrupt_mask;	/* Interrupt Mask */
+	struct dma_interrupt_regs interrupt_clear;	/* Interrupt Clear */
+	struct dmareg		statusInt;		/* Interrupt combined */
+	struct dmareg		rq_srcreg;		/* Src Trans Req */
+	struct dmareg		rq_dstreg;		/* Dst Trans Req */
+	struct dmareg		rq_sgl_srcreg;		/* Sngl Src Trans Req */
+	struct dmareg		rq_sgl_dstreg;		/* Sngl Dst Trans Req */
+	struct dmareg		rq_lst_srcreg;		/* Last Src Trans Req */
+	struct dmareg		rq_lst_dstreg;		/* Last Dst Trans Req */
+	struct dmareg		dma_cfg;		/* DMA Config */
+	struct dmareg		dma_chan_en;		/* DMA Channel Enable */
+	struct dmareg		dma_id;			/* DMA ID */
+	struct dmareg		dma_test;		/* DMA Test */
+	struct dmareg		res1;			/* reserved */
+	struct dmareg		res2;			/* reserved */
+
+	/* DMA Comp Params
+	 * Param 6 = dma_param[0], Param 5 = dma_param[1],
+	 * Param 4 = dma_param[2] ...
+	 */
+	struct dmareg		dma_params[6];
+};
+
+/* Data structure for linked list item */
+struct lli {
+	u32		sar;		/* Source Address */
+	u32		dar;		/* Destination address */
+	u32		llp;		/* Linked List Pointer */
+	struct dmareg	ctl;		/* Control */
+	struct dmareg	dstat;		/* Destination Status */
+};
+
+#define SATA_DWC_DMAC_LLI_SZ		(sizeof(struct lli))
+#define SATA_DWC_DMAC_LLI_NUM		256
+#define SATA_DWC_DMAC_TWIDTH_BYTES	4
+#define SATA_DWC_DMAC_LLI_TBL_SZ	\
+	(SATA_DWC_DMAC_LLI_SZ * SATA_DWC_DMAC_LLI_NUM)
+#define SATA_DWC_DMAC_CTRL_TSIZE_MAX	\
+	(0x00000800 * SATA_DWC_DMAC_TWIDTH_BYTES)
+
+/* DMA Register Operation Bits */
+#define DMA_EN			0x00000001		/* Enable AHB DMA */
+#define DMA_CHANNEL(ch)		(0x00000001 << (ch))	/* Select channel */
+#define DMA_ENABLE_CHAN(ch)	((0x00000001 << (ch)) |			\
+				 ((0x000000001 << (ch)) << 8))
+#define DMA_DISABLE_CHAN(ch)	(0x00000000 | ((0x000000001 << (ch)) << 8))
+
+/* Channel Control Register */
+#define DMA_CTL_BLK_TS(size)	((size) & 0x000000FFF)	/* Blk Transfer size */
+#define DMA_CTL_LLP_SRCEN	0x10000000	/* Blk chain enable Src */
+#define DMA_CTL_LLP_DSTEN	0x08000000	/* Blk chain enable Dst */
+/*
+ * This define is used to set block chaining disabled in the control low
+ * register.  It is already in little endian format so it can be &'d dirctly.
+ * It is essentially: cpu_to_le32(~(DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN))
+ */
+#define DMA_CTL_LLP_DISABLE_LE32 0xffffffe7
+#define DMA_CTL_SMS(num)	((num & 0x3) << 25)	/*Src Master Select*/
+#define DMA_CTL_DMS(num)	((num & 0x3) << 23)	/*Dst Master Select*/
+#define DMA_CTL_TTFC(type)	((type & 0x7) << 20)	/*Type&Flow cntr*/
+#define DMA_CTL_TTFC_P2M_DMAC	0x00000002		/*Per mem,DMAC cntr*/
+#define DMA_CTL_TTFC_M2P_PER	0x00000003		/*Mem per,peri cntr*/
+#define DMA_CTL_SRC_MSIZE(size)	((size & 0x7) << 14)	/*Src Burst Len*/
+#define DMA_CTL_DST_MSIZE(size)	((size & 0x7) << 11)	/*Dst Burst Len*/
+#define DMA_CTL_SINC_INC	0x00000000		/*Src addr incr*/
+#define DMA_CTL_SINC_DEC	0x00000200
+#define DMA_CTL_SINC_NOCHANGE	0x00000400
+#define DMA_CTL_DINC_INC	0x00000000		/*Dst addr incr*/
+#define DMA_CTL_DINC_DEC	0x00000080
+#define DMA_CTL_DINC_NOCHANGE	0x00000100
+#define DMA_CTL_SRC_TRWID(size)	((size & 0x7) << 4)	/*Src Trnsfr Width*/
+#define DMA_CTL_DST_TRWID(size)	((size & 0x7) << 1)	/*Dst Trnsfr Width*/
+#define DMA_CTL_INT_EN		0x00000001		/*Interrupt Enable*/
+
+/* Channel Configuration Register high bits */
+#define DMA_CFG_FCMOD_REQ	0x00000001		/*Flow cntrl req*/
+#define DMA_CFG_PROTCTL		(0x00000003 << 2)	/*Protection cntrl*/
+
+/* Channel Configuration Register low bits */
+#define DMA_CFG_RELD_DST	0x80000000		/*Reload Dst/Src Addr*/
+#define DMA_CFG_RELD_SRC	0x40000000
+#define DMA_CFG_HS_SELSRC	0x00000800		/*SW hndshk Src/Dst*/
+#define DMA_CFG_HS_SELDST	0x00000400
+#define DMA_CFG_FIFOEMPTY       (0x00000001 << 9)	/*FIFO Empty bit*/
+
+/* Assign hardware handshaking interface (x) to dst / sre peripheral */
+#define DMA_CFG_HW_HS_DEST(int_num)	((int_num & 0xF) << 11)
+#define DMA_CFG_HW_HS_SRC(int_num)	((int_num & 0xF) << 7)
+
+/* Channel Linked List Pointer Register */
+#define DMA_LLP_LMS(addr, master)	(((addr) & 0xfffffffc) | (master))
+#define DMA_LLP_AHBMASTER1		0	/* List Master Select */
+#define DMA_LLP_AHBMASTER2		1
+
+#define SATA_DWC_MAX_PORTS	1
+
+#define SATA_DWC_SCR_OFFSET	0x24
+#define SATA_DWC_REG_OFFSET	0x64
+
+/* DWC SATA Registers */
+struct sata_dwc_regs {
+	u32 fptagr;		/* 1st party DMA tag */
+	u32 fpbor;		/* 1st party DMA buffer offset */
+	u32 fptcr;		/* 1st party DMA Xfr count */
+	u32 dmacr;		/* DMA Control */
+	u32 dbtsr;		/* DMA Burst Transac size */
+	u32 intpr;		/* Interrupt Pending */
+	u32 intmr;		/* Interrupt Mask */
+	u32 errmr;		/* Error Mask */
+	u32 llcr;		/* Link Layer Control */
+	u32 phycr;		/* PHY Control */
+	u32 physr;		/* PHY Status */
+	u32 rxbistpd;		/* Recvd BIST pattern def register */
+	u32 rxbistpd1;		/* Recvd BIST data dword1 */
+	u32 rxbistpd2;		/* Recvd BIST pattern data dword2 */
+	u32 txbistpd;		/* Trans BIST pattern def register */
+	u32 txbistpd1;		/* Trans BIST data dword1 */
+	u32 txbistpd2;		/* Trans BIST data dword2 */
+	u32 bistcr;		/* BIST Control Register */
+	u32 bistfctr;		/* BIST FIS Count Register */
+	u32 bistsr;		/* BIST Status Register */
+	u32 bistdecr;		/* BIST Dword Error count register */
+	u32 res[15];		/* Reserved locations */
+	u32 testr;		/* Test Register */
+	u32 versionr;		/* Version Register */
+	u32 idr;		/* ID Register */
+	u32 unimpl[192];	/* Unimplemented */
+	u32 dmadr[256];	/* FIFO Locations in DMA Mode */
+};
+
+#define SCR_SCONTROL_DET_ENABLE		0x00000001
+#define SCR_SSTATUS_DET_PRESENT		0x00000001
+#define SCR_SERROR_DIAG_X		0x04000000
+
+/* DWC SATA Register Operations */
+#define	SATA_DWC_TXFIFO_DEPTH		0x01FF
+#define	SATA_DWC_RXFIFO_DEPTH		0x01FF
+
+#define SATA_DWC_DMACR_TMOD_TXCHEN	0x00000004
+#define	SATA_DWC_DMACR_TXCHEN		(0x00000001 | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define	SATA_DWC_DMACR_RXCHEN		(0x00000002 | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_TX_CLEAR(v)	(((v) & ~SATA_DWC_DMACR_TXCHEN) | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_RX_CLEAR(v)	(((v) & ~SATA_DWC_DMACR_RXCHEN) | \
+						SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_TXRXCH_CLEAR	SATA_DWC_DMACR_TMOD_TXCHEN
+
+#define SATA_DWC_DBTSR_MWR(size)	((size/4) & \
+						SATA_DWC_TXFIFO_DEPTH)
+#define SATA_DWC_DBTSR_MRD(size)	(((size/4) & \
+						SATA_DWC_RXFIFO_DEPTH) << 16)
+#define	SATA_DWC_INTPR_DMAT		0x00000001
+#define SATA_DWC_INTPR_NEWFP		0x00000002
+#define SATA_DWC_INTPR_PMABRT		0x00000004
+#define SATA_DWC_INTPR_ERR		0x00000008
+#define SATA_DWC_INTPR_NEWBIST		0x00000010
+#define SATA_DWC_INTPR_IPF		0x10000000
+#define	SATA_DWC_INTMR_DMATM		0x00000001
+#define SATA_DWC_INTMR_NEWFPM		0x00000002
+#define SATA_DWC_INTMR_PMABRTM		0x00000004
+#define SATA_DWC_INTMR_ERRM		0x00000008
+#define SATA_DWC_INTMR_NEWBISTM		0x00000010
+#define SATA_DWC_LLCR_SCRAMEN		0x00000001
+#define SATA_DWC_LLCR_DESCRAMEN		0x00000002
+#define SATA_DWC_LLCR_RPDEN		0x00000004
+
+/* This is all error bits, zero's are reserved fields. */
+#define SATA_DWC_SERROR_ERR_BITS	0x0FFF0F03
+
+#define SATA_DWC_SCR0_SPD_GET(v)	((v >> 4) & 0x0000000F)
+
+struct sata_dwc_device {
+	struct device		*dev;		/* generic device struct */
+	struct ata_probe_ent	*pe;		/* ptr to probe-ent */
+	struct ata_host		*host;
+	u8			*reg_base;
+	struct sata_dwc_regs	*sata_dwc_regs;	/* DW Synopsys SATA specific */
+	int			irq_dma;
+};
+
+#define SATA_DWC_QCMD_MAX	32
+
+struct sata_dwc_device_port {
+	struct sata_dwc_device	*hsdev;
+	int			cmd_issued[SATA_DWC_QCMD_MAX];
+	struct lli		*llit[SATA_DWC_QCMD_MAX];
+	dma_addr_t		llit_dma[SATA_DWC_QCMD_MAX];
+	u32			dma_chan[SATA_DWC_QCMD_MAX];
+	int			dma_pending[SATA_DWC_QCMD_MAX];
+};
+
+/*
+ * Commonly used DWC SATA driver Macros
+ */
+#define HSDEV_FROM_HOST(host)	((struct sata_dwc_device *) \
+					(host)->private_data)
+#define HSDEV_FROM_AP(ap)	((struct sata_dwc_device *) \
+					(ap)->host->private_data)
+#define HSDEVP_FROM_AP(ap)	((struct sata_dwc_device_port *) \
+					(ap)->private_data)
+#define HSDEV_FROM_QC(qc)	((struct sata_dwc_device *) \
+					(qc)->ap->host->private_data)
+#define HSDEV_FROM_HSDEVP(p)	((struct sata_dwc_device *) \
+					(hsdevp)->hsdev)
+
+enum {
+	SATA_DWC_CMD_ISSUED_NOT		= 0,
+	SATA_DWC_CMD_ISSUED_PEND	= 1,
+	SATA_DWC_CMD_ISSUED_EXEC	= 2,
+	SATA_DWC_CMD_ISSUED_NODATA	= 3,
+
+	SATA_DWC_DMA_PEND_NONE	= 0,
+	SATA_DWC_DMA_PEND_TX		= 1,
+	SATA_DWC_DMA_PEND_RX		= 2,
+};
+
+/*
+ * Globals
+ */
+static u32 sata_dwc_sactive_issued;	/* issued queued ops */
+static u32 sata_dwc_sactive_queued;	/* queued ops */
+static struct ahb_dma_regs *sata_dma_regs;
+static u32 dma_interrupt_count;
+static void *scr_addr_sstatus;
+static struct device *dwc_dev;
+
+/*
+ * Prototypes
+ */
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag);
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+				u32 check_status);
+static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status);
+static void sata_dwc_port_stop(struct ata_port *ap);
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag);
+
+static int dma_dwc_init(struct sata_dwc_device *hsdev, struct resource *res,
+			int irq);
+static void dma_dwc_exit(struct sata_dwc_device *hsdev);
+static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
+			      struct lli *lli, dma_addr_t dma_lli,
+			      void __iomem *addr, int dir);
+static void dma_dwc_xfer_start(int dma_ch);
+static void dma_dwc_terminate_dma(int dma_ch);
+
+static const char *dir_2_txt(enum dma_data_direction dir)
+{
+	switch (dir) {
+	case DMA_BIDIRECTIONAL:
+		return "bi";
+	case DMA_FROM_DEVICE:
+		return "from";
+	case DMA_TO_DEVICE:
+		return "to";
+	case DMA_NONE:
+		return "none";
+	default:
+		return "err";
+	}
+}
+
+static const char *prot_2_txt(enum ata_tf_protocols protocol)
+{
+	switch (protocol) {
+	case ATA_PROT_UNKNOWN:
+		return "unknown";
+	case ATA_PROT_NODATA:
+		return "nodata";
+	case ATA_PROT_PIO:
+		return "pio";
+	case ATA_PROT_DMA:
+		return "dma";
+	case ATA_PROT_NCQ:
+		return "ncq";
+	case ATAPI_PROT_PIO:
+		return "atapi pio";
+	case ATAPI_PROT_NODATA:
+		return "atapi nodata";
+	case ATAPI_PROT_DMA:
+		return "atapi dma";
+	default:
+		return "err";
+	}
+}
+
+inline const char *ata_cmd_2_txt(const struct ata_taskfile *tf)
+{
+	switch (tf->command) {
+	case ATA_CMD_CHK_POWER:
+		return "ATA_CMD_CHK_POWER";
+	case ATA_CMD_EDD:
+		return "ATA_CMD_EDD";
+	case ATA_CMD_FLUSH:
+		return "ATA_CMD_FLUSH";
+	case ATA_CMD_FLUSH_EXT:
+		return "ATA_CMD_FLUSH_EXT";
+	case ATA_CMD_ID_ATA:
+		return "ATA_CMD_ID_ATA";
+	case ATA_CMD_ID_ATAPI:
+		return "ATA_CMD_ID_ATAPI";
+	case ATA_CMD_FPDMA_READ:
+		return "ATA_CMD_FPDMA_READ";
+	case ATA_CMD_FPDMA_WRITE:
+		return "ATA_CMD_FPDMA_WRITE";
+	case ATA_CMD_READ:
+		return "ATA_CMD_READ";
+	case ATA_CMD_READ_EXT:
+		return "ATA_CMD_READ_EXT";
+	case ATA_CMD_WRITE:
+		return "ATA_CMD_WRITE";
+	case ATA_CMD_WRITE_EXT:
+		return "ATA_CMD_WRITE_EXT";
+	case ATA_CMD_PIO_READ:
+		return "ATA_CMD_PIO_READ";
+	case ATA_CMD_PIO_READ_EXT:
+		return "ATA_CMD_PIO_READ_EXT";
+	case ATA_CMD_PIO_WRITE:
+		return "ATA_CMD_PIO_WRITE";
+	case ATA_CMD_PIO_WRITE_EXT:
+		return "ATA_CMD_PIO_WRITE_EXT";
+	case ATA_CMD_SET_FEATURES:
+		return "ATA_CMD_SET_FEATURES";
+	case ATA_CMD_PACKET:
+		return "ATA_CMD_PACKET";
+	default:
+		return "ATA_CMD_???";
+	}
+}
+
+static void sata_dwc_tf_dump(struct ata_taskfile *tf)
+{
+	dev_vdbg(dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags: 0x%lx"
+			"device: %x\n", tf->command, prot_2_txt(tf->protocol),
+			tf->flags, tf->device);
+	dev_vdbg(dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x lbam:"
+			"0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal,
+			tf->lbam, tf->lbah);
+	dev_vdbg(dwc_dev, "hob_feature: 0x%02x hob_nsect: 0x%x hob_lbal: 0x%x "
+			"hob_lbam: 0x%x hob_lbah: 0x%x\n", tf->hob_feature,
+			tf->hob_nsect, tf->hob_lbal, tf->hob_lbam,
+			tf->hob_lbah);
+}
+
+/*******************************************************************************
+ * Function: get_burst_length_encode
+ * arguments: datalength: length in bytes of data
+ * returns value to be programmed in register corrresponding to data length
+ * This value is effectively the log(base 2) of the length
+ ******************************************************************************/
+static inline int get_burst_length_encode(int datalength)
+{
+	int items = datalength >> 2;	/* div by 4 to get lword count */
+
+	if (items >= 64)
+		return 5;
+
+	if (items >= 32)
+		return 4;
+
+	if (items >= 16)
+		return 3;
+
+	if (items >= 8)
+		return 2;
+
+	if (items >= 4)
+		return 1;
+
+	return 0;
+}
+
+static inline void clear_chan_interrupts(int c)
+{
+	out_le32(&(sata_dma_regs->interrupt_clear.tfr.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.block.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.srctran.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.dsttran.low), DMA_CHANNEL(c));
+	out_le32(&(sata_dma_regs->interrupt_clear.error.low), DMA_CHANNEL(c));
+}
+
+/*******************************************************************************
+ * Function: dma_request_channel
+ * arguments: None
+ * returns channel number if available else -1
+ * This function assigns the next available DMA channel from the list to the
+ * requester
+ ******************************************************************************/
+static int dma_request_channel(void)
+{
+	int i;
+	struct ahb_dma_regs *pdma;
+
+	pdma = sata_dma_regs;
+	for (i = 0; i < DMA_NUM_CHANS; i++) {
+		if (!(in_le32(&(pdma->dma_chan_en.low)) & DMA_CHANNEL(i)))
+			return i;
+	}
+
+	/* TODO this should maybe change to warn of info */
+	dev_err(dwc_dev, "%s NO channel chan_en: 0x%08x\n", __func__,
+		in_le32(&(pdma->dma_chan_en.low)));
+
+	return -1;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_interrupt
+ * arguments: irq, dev_id, pt_regs
+ * returns channel number if available else -1
+ * Interrupt Handler for DW AHB SATA DMA
+ ******************************************************************************/
+static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance)
+{
+	int chan;
+	u32 tfr_reg, err_reg;
+	unsigned long flags;
+	struct sata_dwc_device *hsdev =
+		(struct sata_dwc_device *)hsdev_instance;
+	struct ata_host *host = (struct ata_host *)hsdev->host;
+	struct ata_port *ap;
+	struct sata_dwc_device_port *hsdevp;
+	u8 tag = 0;
+	unsigned int port = 0;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	ap = host->ports[port];
+	hsdevp = HSDEVP_FROM_AP(ap);
+	tag = ap->link.active_tag;
+
+	tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low));
+	err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+
+	dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n",
+		tfr_reg, err_reg, hsdevp->dma_pending[tag], port);
+
+	for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+		/* Check for end-of-transfer interrupt. */
+		if (tfr_reg & DMA_CHANNEL(chan)) {
+			/* Each DMA command produces 2 interrupts.  Only
+			 * complete the command after both interrupts have been
+			 * seen. (See sata_dwc_isr())
+			 */
+			dma_interrupt_count++;
+
+			sata_dwc_clear_dmacr(hsdevp, tag);
+
+			if (hsdevp->dma_pending[tag] == 0) {
+				dev_err(ap->dev, "DMA not pending eot=0x%08x "
+					"err=0x%08x tag=0x%02x pending=%d\n",
+					tfr_reg, err_reg, tag,
+					hsdevp->dma_pending[tag]);
+			}
+
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+
+			/* Clear the interrupt */
+			out_le32(&(sata_dma_regs->interrupt_clear.tfr.low),
+				 DMA_CHANNEL(chan));
+		}
+
+		/* Check for error interrupt. */
+		if (err_reg & DMA_CHANNEL(chan)) {
+			/* TODO Need error handler ! */
+			dev_err(ap->dev, "error interrupt err_reg=0x%08x\n",
+					err_reg);
+
+			/* Clear the interrupt. */
+			out_le32(&(sata_dma_regs->interrupt_clear.error.low),
+				 DMA_CHANNEL(chan));
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/*******************************************************************************
+ * Function: dma_request_interrupts
+ * arguments: hsdev
+ * returns status
+ * This function registers ISR for a particular DMA channel interrupt
+ ******************************************************************************/
+static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq)
+{
+	int retval = 0;
+	int chan;
+
+	for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+		/* Unmask error interrupt */
+		out_le32(&sata_dma_regs->interrupt_mask.error.low,
+			 DMA_ENABLE_CHAN(chan));
+
+		/* Unmask end-of-transfer interrupt */
+		out_le32(&sata_dma_regs->interrupt_mask.tfr.low,
+			 DMA_ENABLE_CHAN(chan));
+	}
+
+	retval = request_irq(irq, dma_dwc_interrupt, 0, "SATA DMA", hsdev);
+	if (retval) {
+		dev_err(dwc_dev, "%s: could not get IRQ %d\n", __func__, irq);
+		return -ENODEV;
+	}
+
+	/* Mark this interrupt as requested */
+	hsdev->irq_dma = irq;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function: map_sg_to_lli
+ * arguments: sg: scatter/gather list(sg)
+ *	      num_elems: no of elements in sg list
+ *	      dma_lli: LLI table
+ *	      dest: destination address
+ *	      read: whether the transfer is read or write
+ * returns array of AHB DMA Linked List Items
+ * This function creates a list of LLIs for DMA Xfr and returns the number
+ * of elements in the DMA linked list.
+ *
+ * Note that the Synopsis driver has a comment proposing that better performance
+ * is possible by only enabling interrupts on the last item in the linked list.
+ * However, it seems that could be a problem if an error happened on one of the
+ * first items.  The transfer would halt, but no error interrupt would occur.
+ *
+ * Currently this function sets interrupts enabled for each linked list item:
+ * DMA_CTL_INT_EN.
+ ******************************************************************************/
+static int map_sg_to_lli(struct scatterlist *sg, int num_elems, struct lli *lli,
+			 dma_addr_t dma_lli, void __iomem *dmadr_addr, int dir)
+{
+	int i, idx = 0;
+	int fis_len = 0;
+	dma_addr_t next_llp;
+	int bl;
+	unsigned int dma_ts = 0;
+
+	dev_dbg(dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x "
+		"dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli,
+		(u32)dmadr_addr);
+
+	bl = get_burst_length_encode(AHB_DMA_BRST_DFLT);
+
+	for (i = 0; i < num_elems; i++, sg++) {
+		u32 addr, offset;
+		u32 sg_len, len;
+
+		addr = (u32) sg_dma_address(sg);
+		sg_len = sg_dma_len(sg);
+
+		dev_dbg(dwc_dev, "%s: elem=%d sg_addr=0x%x sg_len=%d\n",
+			__func__, i, addr, sg_len);
+
+		while (sg_len) {
+
+			if (idx >= SATA_DWC_DMAC_LLI_NUM) {
+				/* The LLI table is not large enough. */
+				dev_err(dwc_dev, "LLI table overrun (idx=%d)\n",
+						idx);
+				break;
+			}
+			len = (sg_len > SATA_DWC_DMAC_CTRL_TSIZE_MAX) ?
+				SATA_DWC_DMAC_CTRL_TSIZE_MAX : sg_len;
+
+			offset = addr & 0xffff;
+			if ((offset + sg_len) > 0x10000)
+				len = 0x10000 - offset;
+
+			/*
+			 * Make sure a LLI block is not created that will span a
+			 * 8K max FIS boundary.  If the block spans such a FIS
+			 * boundary, there is a chance that a DMA burst will
+			 * cross that boundary -- this results in an error in
+			 * the host controller.
+			 */
+			if (fis_len + len > 8192) {
+				dev_dbg(dwc_dev, "SPLITTING: fis_len=%d(0x%x) "
+					"len=%d(0x%x)\n", fis_len, fis_len,
+					len, len);
+				len = 8192 - fis_len;
+				fis_len = 0;
+			} else {
+				fis_len += len;
+			}
+			if (fis_len == 8192)
+				fis_len = 0;
+
+			/*
+			 * Set DMA addresses and lower half of control register
+			 * based on direction.
+			 */
+			if (dir == DMA_FROM_DEVICE) {
+				lli[idx].dar = cpu_to_le32(addr);
+				lli[idx].sar = cpu_to_le32((u32)dmadr_addr);
+
+				lli[idx].ctl.low = cpu_to_le32(
+					DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) |
+					DMA_CTL_SMS(0) |
+					DMA_CTL_DMS(1) |
+					DMA_CTL_SRC_MSIZE(bl) |
+					DMA_CTL_DST_MSIZE(bl) |
+					DMA_CTL_SINC_NOCHANGE |
+					DMA_CTL_SRC_TRWID(2) |
+					DMA_CTL_DST_TRWID(2) |
+					DMA_CTL_INT_EN |
+					DMA_CTL_LLP_SRCEN |
+					DMA_CTL_LLP_DSTEN);
+			} else {	/* DMA_TO_DEVICE */
+				lli[idx].sar = cpu_to_le32(addr);
+				lli[idx].dar = cpu_to_le32((u32)dmadr_addr);
+
+				lli[idx].ctl.low = cpu_to_le32(
+					DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) |
+					DMA_CTL_SMS(1) |
+					DMA_CTL_DMS(0) |
+					DMA_CTL_SRC_MSIZE(bl) |
+					DMA_CTL_DST_MSIZE(bl) |
+					DMA_CTL_DINC_NOCHANGE |
+					DMA_CTL_SRC_TRWID(2) |
+					DMA_CTL_DST_TRWID(2) |
+					DMA_CTL_INT_EN |
+					DMA_CTL_LLP_SRCEN |
+					DMA_CTL_LLP_DSTEN);
+			}
+
+			dev_dbg(dwc_dev, "%s setting ctl.high len: 0x%08x val: "
+					"0x%08x\n", __func__, len,
+					DMA_CTL_BLK_TS(len / 4));
+
+			/* Program the LLI CTL high register */
+			dma_ts = DMA_CTL_BLK_TS(len / 4);
+			lli[idx].ctl.high = cpu_to_le32(dma_ts);
+
+			/* Program the next pointer.  The next pointer must be
+			 * the physical address, not the virtual address.
+			 */
+			next_llp = (dma_lli + ((idx + 1) * sizeof(struct lli)));
+
+			/* The last 2 bits encode the list master select. */
+			next_llp = DMA_LLP_LMS(next_llp, DMA_LLP_AHBMASTER2);
+
+			lli[idx].llp = cpu_to_le32(next_llp);
+
+			idx++;
+			sg_len -= len;
+			addr += len;
+		}
+	}
+
+	/*
+	 * The last next ptr has to be zero and the last control low register
+	 * has to have LLP_SRC_EN and LLP_DST_EN (linked list pointer source
+	 * and destination enable) set back to 0 (disabled.)  This is what tells
+	 * the core that this is the last item in the linked list.
+	 */
+	if (idx) {
+		lli[idx-1].llp = 0x00000000;
+		lli[idx-1].ctl.low &= DMA_CTL_LLP_DISABLE_LE32;
+
+		/* Flush cache to memory */
+		dma_cache_sync(NULL, lli, (sizeof(struct lli) * idx),
+			       DMA_BIDIRECTIONAL);
+	}
+
+	return idx;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_xfer_start
+ * arguments: Channel number
+ * Return : None
+ * Enables the DMA channel
+ ******************************************************************************/
+static void dma_dwc_xfer_start(int dma_ch)
+{
+	/* Enable the DMA channel */
+	out_le32(&(sata_dma_regs->dma_chan_en.low),
+		 in_le32(&(sata_dma_regs->dma_chan_en.low)) |
+		 DMA_ENABLE_CHAN(dma_ch));
+}
+
+static int dma_dwc_channel_enabled(int ch)
+{
+	if (in_le32(&(sata_dma_regs->dma_chan_en.low)) & DMA_CHANNEL(ch))
+		return 1;
+
+	return 0;
+}
+
+static void dma_dwc_terminate_dma(int dma_ch)
+{
+	int enabled = dma_dwc_channel_enabled(dma_ch);
+
+	dev_info(dwc_dev, "%s terminate DMA on channel=%d enabled=%d\n",
+		 __func__, dma_ch, enabled);
+
+	if (enabled)  {
+		out_le32(&(sata_dma_regs->dma_chan_en.low),
+			 DMA_DISABLE_CHAN(dma_ch));
+
+		do {
+			enabled = dma_dwc_channel_enabled(dma_ch);
+		} while (enabled);
+	}
+}
+
+static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
+			      struct lli *lli, dma_addr_t dma_lli,
+			      void __iomem *addr, int dir)
+{
+	int dma_ch;
+	int num_lli;
+
+	/* Acquire DMA channel */
+	dma_ch = dma_request_channel();
+	if (dma_ch == -1) {
+		dev_err(dwc_dev, "%s: dma channel unavailable\n", __func__);
+		return -EAGAIN;
+	}
+
+	/* Convert SG list to linked list of items (LLIs) for AHB DMA */
+	num_lli = map_sg_to_lli(sg, num_elems, lli, dma_lli, addr, dir);
+
+	dev_dbg(dwc_dev, "%s sg: 0x%p, count: %d lli: %p dma_lli: 0x%0xlx addr:"
+		" %p lli count: %d\n", __func__, sg, num_elems, lli,
+		(u32)dma_lli, addr, num_lli);
+
+	/* Clear channel interrupts */
+	clear_chan_interrupts(dma_ch);
+
+	/* Program the CFG register. */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high),
+		 DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ);
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0);
+
+	/* Program the address of the linked list */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low),
+		 DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER2));
+
+	/* Program the CTL register with src enable / dst enable */
+	out_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low),
+		 DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_exit
+ * arguments: None
+ * returns status
+ * This function exits the SATA DMA driver
+ ******************************************************************************/
+static void dma_dwc_exit(struct sata_dwc_device *hsdev)
+{
+	dev_dbg(dwc_dev, "%s:\n", __func__);
+	if (sata_dma_regs)
+		iounmap(sata_dma_regs);
+
+	if (hsdev->irq_dma)
+		free_irq(hsdev->irq_dma, hsdev);
+}
+
+/*******************************************************************************
+ * Function: dma_dwc_init
+ * arguments: hsdev
+ * returns status
+ * This function initializes the SATA DMA driver
+ ******************************************************************************/
+static int dma_dwc_init(struct sata_dwc_device *hsdev, struct resource *res,
+			int irq)
+{
+	int err;
+
+	sata_dma_regs = ioremap(res->start, res->end - res->start + 1);
+	if (!sata_dma_regs) {
+		dev_err(dwc_dev, "%s: ioremap failed\n", __func__);
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	err = dma_request_interrupts(hsdev, irq);
+	if (err) {
+		dev_err(dwc_dev, "%s: dma_request_interrupts returns %d\n",
+			__func__, err);
+		goto error_out;
+	}
+
+	/* Enabe DMA */
+	out_le32(&(sata_dma_regs->dma_cfg.low), DMA_EN);
+
+	dev_notice(dwc_dev, "DMA initialized\n");
+	dev_dbg(dwc_dev, "SATA DMA registers=0x%p\n", sata_dma_regs);
+
+	return 0;
+
+error_out:
+	dma_dwc_exit(hsdev);
+
+	return err;
+}
+
+static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
+{
+	if (scr > SCR_NOTIFICATION) {
+		dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+				__func__, scr);
+		return -EINVAL;
+	}
+
+	*val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4));
+	dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n",
+		__func__, link->ap->print_id, scr, *val);
+
+	return 0;
+}
+
+static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val)
+{
+	dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n",
+		__func__, link->ap->print_id, scr, val);
+	if (scr > SCR_NOTIFICATION) {
+		dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+				__func__, scr);
+		return -EINVAL;
+	}
+	out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val);
+
+	return 0;
+}
+
+static inline u32 core_scr_read(unsigned int scr)
+{
+	return in_le32((void __iomem *)scr_addr_sstatus + (scr * 4));
+}
+
+static inline void core_scr_write(unsigned int scr, u32 val)
+{
+	out_le32((void __iomem *)scr_addr_sstatus + (scr * 4), val);
+}
+
+static inline void clear_serror(void)
+{
+	out_le32((void __iomem *)scr_addr_sstatus + 4,
+		 in_le32((void __iomem *)scr_addr_sstatus + 4));
+}
+
+static inline void clear_intpr(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intpr,
+		 in_le32(&hsdev->sata_dwc_regs->intpr));
+}
+
+static inline void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit)
+{
+	out_le32(&hsdev->sata_dwc_regs->intpr,
+		 in_le32(&hsdev->sata_dwc_regs->intpr));
+}
+
+static inline void disable_err_irq(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 in_le32(&hsdev->sata_dwc_regs->intmr) & ~SATA_DWC_INTMR_ERRM);
+	out_le32(&hsdev->sata_dwc_regs->errmr, ~SATA_DWC_SERROR_ERR_BITS);
+}
+
+static inline void enable_err_irq(struct sata_dwc_device *hsdev)
+{
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 in_le32(&hsdev->sata_dwc_regs->intmr) | SATA_DWC_INTMR_ERRM);
+	out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS);
+}
+
+static inline u32 qcmd_tag_to_mask(u8 tag)
+{
+	return 0x00000001 << (tag & 0x1f);
+}
+
+/* See ahci.c */
+static void sata_dwc_error_intr(struct ata_port *ap,
+				struct sata_dwc_device *hsdev, uint intpr)
+{
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	struct ata_eh_info *ehi = &ap->link.eh_info;
+	unsigned int err_mask = 0, action = 0;
+	struct ata_queued_cmd *qc;
+	u32 serror;
+	u8 status, tag;
+	u32 err_reg;
+
+	ata_ehi_clear_desc(ehi);
+
+	serror = core_scr_read(SCR_ERROR);
+	status = ap->ops->sff_check_status(ap);
+
+	err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+	tag = ap->link.active_tag;
+
+	dev_err(ap->dev, "%s SCR_ERROR=0x%08x intpr=0x%08x status=0x%08x "
+		"dma_intp=%d pending=%d issued=%d dma_err_status=0x%08x\n",
+		__func__, serror, intpr, status, dma_interrupt_count,
+		hsdevp->dma_pending[tag], hsdevp->cmd_issued[tag], err_reg);
+
+	/* Clear error register and interrupt bit */
+	clear_serror();
+	clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR);
+
+	/* This is the only error happening now. */
+	/* TODO check for exact error */
+	err_mask |= AC_ERR_HOST_BUS;
+	action |= ATA_EH_RESET;
+
+	/* Pass this on to EH */
+	ehi->serror |= serror;
+	ehi->action |= action;
+
+	qc = ata_qc_from_tag(ap, tag);
+	if (qc)
+		qc->err_mask |= err_mask;
+	else
+		ehi->err_mask |= err_mask;
+
+	ata_port_abort(ap);
+
+	/*
+	  if (irq_stat & PORT_IRQ_FREEZE)
+	  ata_port_freeze(ap);
+	  else
+	  ata_port_abort(ap);
+	*/
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_isr
+ * arguments : irq, void *dev_instance, struct pt_regs *regs
+ * Return value : irqreturn_t - status of IRQ
+ * This Interrupt handler called via port ops registered function.
+ * .irq_handler = sata_dwc_isr
+ ******************************************************************************/
+static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
+{
+	struct ata_host *host = (struct ata_host *)dev_instance;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
+	struct ata_port *ap;
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	u8 status, tag;
+	int handled, num_processed, port = 0;
+	uint intpr, sactive, sactive2, tag_mask;
+	struct sata_dwc_device_port *hsdevp;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Read the interrupt register */
+	intpr = in_le32(&hsdev->sata_dwc_regs->intpr);
+
+	ap = host->ports[port];
+	hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
+		ap->link.active_tag);
+
+	/* Check for error interrupt */
+	if (intpr & SATA_DWC_INTPR_ERR) {
+		sata_dwc_error_intr(ap, hsdev, intpr);
+		handled = 1;
+		goto DONE;
+	}
+
+	/* Check for DMA SETUP FIS (FP DMA) interrupt */
+	if (intpr & SATA_DWC_INTPR_NEWFP) {
+		clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);
+
+		tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr));
+		dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
+		if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND)
+			dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);
+
+		sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag);
+
+		qc = ata_qc_from_tag(ap, tag);
+		/* Start FP DMA for NCQ command.  At this point the tag is the
+		 * active tag.  It is the tag that matches the command about to
+		 * be completed.
+		 */
+		qc->ap->link.active_tag = tag;
+		sata_dwc_bmdma_start_by_tag(qc, tag);
+
+		handled = 1;
+		goto DONE;
+	}
+
+	sactive = core_scr_read(SCR_ACTIVE);
+	tag_mask = (sata_dwc_sactive_issued | sactive) ^ sactive;
+
+	/* If no sactive issued and tag_mask is zero then this is not NCQ */
+	if (sata_dwc_sactive_issued == 0 && tag_mask == 0) {
+		tag = 0;
+		qc = ata_qc_from_tag(ap, tag);
+
+		/* DEV interrupt w/ no active qc? */
+		if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
+			dev_err(ap->dev, "%s intr with no active qc qc=%p\n",
+				__func__, qc);
+			ata_sff_check_status(ap);
+			handled = 1;
+			goto DONE;
+		}
+
+		status = ap->ops->sff_check_status(ap);
+
+		qc->ap->link.active_tag = tag;
+		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;
+
+		if (status & ATA_ERR) {
+			dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
+			sata_dwc_qc_complete(ap, qc, 1);
+			handled = 1;
+			goto DONE;
+		}
+
+		dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
+			__func__, prot_2_txt(qc->tf.protocol));
+DRVSTILLBUSY:
+		if (ata_is_dma(qc->tf.protocol)) {
+			int dma_flag = hsdevp->dma_pending[tag];
+			/* Each DMA transaction produces 2 interrupts.  The DMAC
+			 * transfer complete interrupt and the SATA controller
+			 * operation done interrupt. The command should be
+			 * completed only after both interrupts are seen.
+			 */
+			dma_interrupt_count++;
+			if (dma_flag == SATA_DWC_DMA_PEND_NONE) {
+				dev_err(ap->dev, "%s: DMA not pending "
+					"intpr=0x%08x status=0x%08x pend=%d\n",
+					__func__, intpr, status, dma_flag);
+			}
+
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+		} else {
+			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
+				goto DRVSTILLBUSY;
+		}
+
+		handled = 1;
+		goto DONE;
+	}
+
+	/*
+	 * This is a NCQ command.  At this point we need to figure out for which
+	 * tags we have gotten a completion interrupt.  One interrupt may serve
+	 * as completion for more than one operation when commands are queued
+	 * (NCQ).  We need to process each completed command.
+	 */
+
+PROCESS:  /* process completed commands */
+	sactive = core_scr_read(SCR_ACTIVE);
+	tag_mask = (sata_dwc_sactive_issued | sactive) ^ sactive;
+
+	if (sactive != 0 || sata_dwc_sactive_issued > 1 || tag_mask > 1) {
+		dev_dbg(ap->dev, "%s NCQ: sactive=0x%08x  sactive_issued=0x%08x"
+			" tag_mask=0x%08x\n", __func__, sactive,
+			sata_dwc_sactive_issued, tag_mask);
+	}
+
+	if ((tag_mask | sata_dwc_sactive_issued) != sata_dwc_sactive_issued) {
+		dev_warn(ap->dev, "Bad tag mask?  sactive=0x%08x "
+			 "sata_dwc_sactive_issued=0x%08x  tag_mask=0x%08x\n",
+			 sactive, sata_dwc_sactive_issued, tag_mask);
+	}
+
+	/* read just to clear ... not bad if currently still busy */
+	status = ap->ops->sff_check_status(ap);
+	dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);
+
+	tag = 0;
+	num_processed = 0;
+	while (tag_mask) {
+		num_processed++;
+		while (!(tag_mask & 0x00000001)) {
+			tag++;
+			tag_mask <<= 1;
+		}
+		tag_mask &= (~0x00000001);
+		qc = ata_qc_from_tag(ap, tag);
+
+		/* To be picked up by completion functions */
+		qc->ap->link.active_tag = tag;
+		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;
+
+		/* Let libata/scsi layers handle error */
+		if (status & ATA_ERR) {
+			dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n",
+					 __func__, status);
+
+			sata_dwc_qc_complete(ap, qc, 1);
+			handled = 1;
+			goto DONE;
+		}
+
+		/* Process completed command */
+		dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__,
+			prot_2_txt(qc->tf.protocol));
+		if (ata_is_dma(qc->tf.protocol)) {
+			dma_interrupt_count++;
+			if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_NONE)
+				dev_warn(ap->dev,
+					"%s: DMA not pending?\n", __func__);
+			if ((dma_interrupt_count % 2) == 0)
+				sata_dwc_dma_xfer_complete(ap, 1);
+		} else {
+			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
+				goto STILLBUSY;
+		}
+		continue;
+
+STILLBUSY:
+		ap->stats.idle_irq++;
+		dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n",
+				ap->print_id);
+	} /* while tag_mask */
+
+	/*
+	 * Check to see if any commands completed while we were processing our
+	 * initial set of completed commands (reading of status clears
+	 * interrupts, so we might miss a completed command interrupt if one
+	 * came in while we were processing:
+	 * we read status as part of processing a completed command).
+	 */
+	sactive2 = core_scr_read(SCR_ACTIVE);
+	if (sactive2 != sactive) {
+		dev_dbg(ap->dev, "More finished - sactive=0x%x sactive2=0x%x\n",
+			sactive, sactive2);
+		goto PROCESS;
+	}
+	handled = 1;
+
+DONE:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag)
+{
+	struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp);
+
+	if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_RX) {
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+			 SATA_DWC_DMACR_RX_CLEAR(
+				 in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+	} else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_TX) {
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+			 SATA_DWC_DMACR_TX_CLEAR(
+				 in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+	} else {
+		/* This should not happen, it indicates the driver is out of
+		 * sync.  If it does happen, clear dmacr anyway.
+		 */
+		dev_err(dwc_dev, "%s DMA protocol RX and TX DMA not pending "
+			"tag=0x%02x pending=%d dmacr: 0x%08x\n",
+			__func__, tag, hsdevp->dma_pending[tag],
+			in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+		out_le32(&(hsdev->sata_dwc_regs->dmacr),
+				SATA_DWC_DMACR_TXRXCH_CLEAR);
+	}
+}
+
+static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status)
+{
+	struct ata_queued_cmd *qc;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	u8 tag = 0;
+
+	tag = ap->link.active_tag;
+	qc = ata_qc_from_tag(ap, tag);
+
+#ifdef DEBUG_NCQ
+	if (tag > 0) {
+		dev_info(ap->dev, "%s tag=%u cmd=0x%02x dma dir=%s proto=%s "
+			 "dmacr=0x%08x\n", __func__, qc->tag, qc->tf.command,
+			 dir_2_txt(qc->dma_dir), prot_2_txt(qc->tf.protocol),
+			 in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+	}
+#endif
+
+	if (ata_is_dma(qc->tf.protocol)) {
+		if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_NONE) {
+			dev_err(ap->dev, "%s DMA protocol RX and TX DMA not "
+				"pending dmacr: 0x%08x\n", __func__,
+				in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+		}
+
+		hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_NONE;
+		sata_dwc_qc_complete(ap, qc, check_status);
+		ap->link.active_tag = ATA_TAG_POISON;
+	} else {
+		sata_dwc_qc_complete(ap, qc, check_status);
+	}
+}
+
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+				u32 check_status)
+{
+	u8 status = 0;
+	int i = 0;
+	u32 mask = 0x0;
+	u8 tag = qc->tag;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	u32 serror;
+
+	dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status);
+
+	if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_TX)
+		dev_err(ap->dev, "TX DMA PENDING\n");
+	else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PEND_RX)
+		dev_err(ap->dev, "RX DMA PENDING\n");
+
+	if (check_status) {
+		i = 0;
+		do {
+			/* check main status, clearing INTRQ */
+			status = ap->ops->sff_check_status(ap);
+			if (status & ATA_BUSY) {
+				dev_dbg(ap->dev, "STATUS BUSY (0x%02x) [%d]\n",
+						status, i);
+			}
+			if (++i > 10)
+				break;
+		} while (status & ATA_BUSY);
+
+		status = ap->ops->sff_check_status(ap);
+		if (unlikely(status & ATA_BUSY))
+			dev_err(ap->dev, "QC complete cmd=0x%02x STATUS BUSY "
+				"(0x%02x) [%d]\n", qc->tf.command, status, i);
+		serror = core_scr_read(SCR_ERROR);
+		if (serror & SATA_DWC_SERROR_ERR_BITS)
+			dev_err(ap->dev, "****** SERROR=0x%08x ******\n",
+				serror);
+	}
+	dev_dbg(ap->dev, "QC complete cmd=0x%02x status=0x%02x ata%u: "
+		"protocol=%d\n", qc->tf.command, status, ap->print_id,
+		qc->tf.protocol);
+
+	/* clear active bit */
+	mask = (~(qcmd_tag_to_mask(tag)));
+	sata_dwc_sactive_queued = sata_dwc_sactive_queued & mask;
+	sata_dwc_sactive_issued = sata_dwc_sactive_issued & mask;
+
+	/* Complete taskfile transaction (does not read SCR registers) */
+	ata_qc_complete(qc);
+
+	return 0;
+}
+
+static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev)
+{
+	/* Enable selective interrupts by setting the interrupt mask register */
+	out_le32(&hsdev->sata_dwc_regs->intmr,
+		 SATA_DWC_INTMR_ERRM |
+		 SATA_DWC_INTMR_NEWFPM |
+		 SATA_DWC_INTMR_PMABRTM |
+		 SATA_DWC_INTMR_DMATM);
+
+	/* Unmask the error bits that should trigger an error interrupt by
+	 * setting the error mask register.
+	 */
+	out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS);
+
+	dev_dbg(dwc_dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", __func__,
+		in_le32(&hsdev->sata_dwc_regs->intmr),
+		in_le32(&hsdev->sata_dwc_regs->errmr));
+}
+
+static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base)
+{
+	port->cmd_addr = (void *)base + 0x00;
+	port->data_addr = (void *)base + 0x00;
+
+	port->error_addr = (void *)base + 0x04;
+	port->feature_addr = (void *)base + 0x04;
+
+	port->nsect_addr = (void *)base + 0x08;
+
+	port->lbal_addr = (void *)base + 0x0c;
+	port->lbam_addr = (void *)base + 0x10;
+	port->lbah_addr = (void *)base + 0x14;
+
+	port->device_addr = (void *)base + 0x18;
+	port->command_addr = (void *)base + 0x1c;
+	port->status_addr = (void *)base + 0x1c;
+
+	port->altstatus_addr = (void *)base + 0x20;
+	port->ctl_addr = (void *)base + 0x20;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_port_start
+ * arguments : struct ata_ioports *port
+ * Return value : returns 0 if success, error code otherwise
+ * This function allocates the scatter gather LLI table for AHB DMA
+ ******************************************************************************/
+static int sata_dwc_port_start(struct ata_port *ap)
+{
+	int err = 0;
+	struct sata_dwc_device *hsdev;
+	struct sata_dwc_device_port *hsdevp = NULL;
+	struct device *pdev;
+	u32 sstatus;
+	int i;
+
+	hsdev = HSDEV_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s: port_no=%d\n", __func__, ap->port_no);
+
+	hsdev->host = ap->host;
+	pdev = ap->host->dev;
+	if (!pdev) {
+		dev_err(ap->dev, "%s: no ap->host->dev\n", __func__);
+		err = -ENODEV;
+		goto CLEANUP;
+	}
+
+	/* Allocate Port Struct */
+	hsdevp = kmalloc(sizeof(*hsdevp), GFP_KERNEL);
+	if (!hsdevp) {
+		dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__);
+		err = -ENOMEM;
+		goto CLEANUP;
+	}
+	memset(hsdevp, 0, sizeof(*hsdevp));
+	hsdevp->hsdev = hsdev;
+
+	for (i = 0; i < SATA_DWC_QCMD_MAX; i++)
+		hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT;
+
+	ap->prd = 0;	/* set these so libata doesn't use them */
+	ap->prd_dma = 0;
+
+	/*
+	 * DMA - Assign scatter gather LLI table. We can't use the libata
+	 * version since it's PRD is IDE PCI specific.
+	 */
+	for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+		hsdevp->llit[i] = dma_alloc_coherent(pdev,
+						     SATA_DWC_DMAC_LLI_TBL_SZ,
+						     &(hsdevp->llit_dma[i]),
+						     GFP_ATOMIC);
+		if (!hsdevp->llit[i]) {
+			dev_err(ap->dev, "%s: dma_alloc_coherent failed size "
+				"0x%x\n", __func__, SATA_DWC_DMAC_LLI_TBL_SZ);
+			err = -ENOMEM;
+			goto CLEANUP;
+		}
+	}
+
+	if (ap->port_no == 0)  {
+		dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n",
+				__func__);
+
+		out_le32(&hsdev->sata_dwc_regs->dmacr,
+			 SATA_DWC_DMACR_TXRXCH_CLEAR);
+
+		dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", __func__);
+		out_le32(&hsdev->sata_dwc_regs->dbtsr,
+			 (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) |
+			  SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)));
+	}
+
+	/* Clear any error bits before libata starts issuing commands */
+	clear_serror();
+
+	ap->private_data = hsdevp;
+
+	/* Are we in Gen I or II */
+	sstatus = core_scr_read(SCR_STATUS);
+	switch (SATA_DWC_SCR0_SPD_GET(sstatus)) {
+	case 0x0:
+		dev_info(ap->dev, "**** No neg speed (nothing attached?) \n");
+		break;
+	case 0x1:
+		dev_info(ap->dev, "**** GEN I speed rate negotiated \n");
+		break;
+	case 0x2:
+		dev_info(ap->dev, "**** GEN II speed rate negotiated \n");
+		break;
+	}
+
+CLEANUP:
+	if (err) {
+		kfree(hsdevp);
+		sata_dwc_port_stop(ap);
+		dev_dbg(ap->dev, "%s: fail\n", __func__);
+	} else {
+		dev_dbg(ap->dev, "%s: done\n", __func__);
+	}
+
+	return err;
+}
+
+static void sata_dwc_port_stop(struct ata_port *ap)
+{
+	int i;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id);
+
+	if (hsdevp && hsdev) {
+		/* deallocate LLI table */
+		for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+			dma_free_coherent(ap->host->dev,
+					  SATA_DWC_DMAC_LLI_TBL_SZ,
+					  hsdevp->llit[i], hsdevp->llit_dma[i]);
+		}
+
+		kfree(hsdevp);
+	}
+	ap->private_data = NULL;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_exec_command_by_tag
+ * arguments : ata_port *ap, ata_taskfile *tf, u8 tag, u32 cmd_issued
+ * Return value : None
+ * This function keeps track of individual command tag ids and calls
+ * ata_exec_command in libata
+ ******************************************************************************/
+static void sata_dwc_exec_command_by_tag(struct ata_port *ap,
+					 struct ata_taskfile *tf,
+					 u8 tag, u32 cmd_issued)
+{
+	unsigned long flags;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+	dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command,
+		ata_cmd_2_txt(tf), tag);
+
+	spin_lock_irqsave(&ap->host->lock, flags);
+	hsdevp->cmd_issued[tag] = cmd_issued;
+	spin_unlock_irqrestore(&ap->host->lock, flags);
+
+	/* Clear SError before executing a new command.
+	 *
+	 * TODO if we read a PM's registers now, we will throw away the task
+	 * file values loaded into the shadow registers for this command.
+	 *
+	 * sata_dwc_scr_write and read can not be used here. Clearing the PM
+	 * managed SError register for the disk needs to be done before the
+	 * task file is loaded.
+	 */
+	clear_serror();
+	ata_sff_exec_command(ap, tf);
+}
+
+static void sata_dwc_bmdma_setup_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	sata_dwc_exec_command_by_tag(qc->ap, &qc->tf, tag,
+				     SATA_DWC_CMD_ISSUED_PEND);
+}
+
+static void sata_dwc_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	u8 tag = qc->tag;
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+			__func__, qc->ap->link.sactive, tag);
+	} else {
+		tag = 0;
+	}
+
+	sata_dwc_bmdma_setup_by_tag(qc, tag);
+}
+
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	int start_dma;
+	u32 reg, dma_chan;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_QC(qc);
+	struct ata_port *ap = qc->ap;
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	int dir = qc->dma_dir;
+	dma_chan = hsdevp->dma_chan[tag];
+
+	/* Used for ata_bmdma_start(qc) -- we are not BMDMA compatible */
+
+	if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_NOT) {
+		start_dma = 1;
+		if (dir == DMA_TO_DEVICE)
+			hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_TX;
+		else
+			hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_RX;
+	} else {
+		dev_err(ap->dev, "%s: Command not pending cmd_issued=%d "
+			"(tag=%d) - DMA NOT started\n", __func__,
+			hsdevp->cmd_issued[tag], tag);
+		start_dma = 0;
+	}
+
+	dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s "
+			"start_dma? %x\n", __func__, qc, tag, qc->tf.command,
+			dir_2_txt(qc->dma_dir), start_dma);
+	sata_dwc_tf_dump(&(qc->tf));
+
+	if (start_dma) {
+		reg = core_scr_read(SCR_ERROR);
+		if (reg & SATA_DWC_SERROR_ERR_BITS) {
+			dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n",
+				__func__, reg);
+		}
+
+		if (dir == DMA_TO_DEVICE)
+			out_le32(&hsdev->sata_dwc_regs->dmacr,
+					SATA_DWC_DMACR_TXCHEN);
+		else
+			out_le32(&hsdev->sata_dwc_regs->dmacr,
+					SATA_DWC_DMACR_RXCHEN);
+
+		/* Enable AHB DMA transfer on the specified channel */
+		dma_dwc_xfer_start(dma_chan);
+	}
+}
+
+static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc)
+{
+	u8 tag = qc->tag;
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+			__func__, qc->ap->link.sactive, tag);
+	} else {
+		tag = 0;
+	}
+
+	dev_dbg(qc->ap->dev, "%s\n", __func__);
+	sata_dwc_bmdma_start_by_tag(qc, tag);
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_qc_prep_by_tag
+ * arguments : ata_queued_cmd *qc, u8 tag
+ * Return value : None
+ * qc_prep for a particular queued command based on tag
+ ******************************************************************************/
+static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+	struct scatterlist *sg = qc->sg;
+	struct ata_port *ap = qc->ap;
+	u32 dma_chan;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	int dir;
+	int err;
+
+	dir = qc->dma_dir;
+
+	if ((dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO))
+		return;
+
+	dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n",
+		__func__, ap->port_no, dir_2_txt(dir), qc->n_elem);
+
+	dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag],
+			hsdevp->llit_dma[tag],
+			(void *__iomem)(&hsdev->sata_dwc_regs->dmadr),
+			dir);
+	if (dma_chan < 0) {
+		dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n",
+			__func__, err);
+		return;
+	}
+
+	hsdevp->dma_chan[tag] = dma_chan;
+}
+
+static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+#ifdef DEBUG_NCQ
+	if (qc->tag > 0 || ap->link.sactive > 1) {
+		dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d prot=%s"
+			" ap active_tag=0x%08x ap sactive=0x%08x\n",
+			__func__, ap->print_id, qc->tf.command,
+			ata_cmd_2_txt(&qc->tf), qc->tag,
+			prot_2_txt(qc->tf.protocol), ap->link.active_tag,
+			ap->link.sactive);
+	}
+#endif
+
+	if (ata_is_ncq(qc->tf.protocol)) {
+		ap->ops->sff_tf_load(ap, &qc->tf);
+		sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag,
+					     SATA_DWC_CMD_ISSUED_PEND);
+	} else {
+		ata_sff_qc_issue(qc);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_eng_timeout
+ * arguments : ata_port *ap
+ * Return value : None
+ * error handler for DMA time out
+ * ata_eng_timeout(ap) -- this does bmdma stuff which can not be done by this
+ * driver. SEE ALSO ata_qc_timeout(ap)
+ ******************************************************************************/
+static void sata_dwc_eng_timeout(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+	struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+	struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+	u8  tag;
+	uint mask = 0x0;
+	unsigned long flags;
+	u32 serror, intpr, dma_ch;
+
+	tag = ap->link.active_tag;
+	dma_ch = hsdevp->dma_chan[tag];
+	qc = ata_qc_from_tag(ap, tag);
+
+	dev_err(ap->dev, "%s: id=%d active_tag=%d qc=%p dma_chan=%d\n",
+		__func__, ap->print_id, tag, qc, dma_ch);
+
+	if (!qc) {
+		dev_err(ap->dev,
+			"%s: timeout without queued command\n", __func__);
+		return;
+	}
+
+	intpr = in_le32(&hsdev->sata_dwc_regs->intpr);
+	serror = core_scr_read(SCR_ERROR);
+
+	dev_err(ap->dev, "intpr=0x%08x serror=0x%08x\n", intpr, serror);
+
+	/* If there are no error bits set, can we just pass this on to eh? */
+	if (!(serror & SATA_DWC_SERROR_ERR_BITS) &&
+	    !(intpr & SATA_DWC_INTPR_ERR)) {
+
+		spin_lock_irqsave(&ap->host->lock, flags);
+		if (dma_dwc_channel_enabled(dma_ch))
+			dma_dwc_terminate_dma(dma_ch);
+
+		hsdevp->dma_pending[tag] = SATA_DWC_DMA_PEND_NONE;
+
+		/* clear active bit */
+		mask = (~(qcmd_tag_to_mask(tag)));
+		sata_dwc_sactive_queued = sata_dwc_sactive_queued & mask;
+		sata_dwc_sactive_issued = sata_dwc_sactive_issued & mask;
+
+		spin_unlock_irqrestore(&ap->host->lock, flags);
+	} else {
+		/* This is wrong, what really needs to be done is a reset. */
+
+		spin_lock_irqsave(ap->lock, flags);
+
+		if (ata_is_dma(qc->tf.protocol)) {
+			/* disable DMAC */
+			dma_dwc_terminate_dma(dma_ch);
+		}
+
+		spin_unlock_irqrestore(ap->lock, flags);
+	}
+
+	WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE));
+	if (qc->flags & ATA_QCFLAG_ACTIVE) {
+		qc->err_mask |= AC_ERR_TIMEOUT;
+		/*
+		 * test-only: The original code (AMCC: 2.6.19) called
+		 * ata_eng_timeout(ap) here. This function is not available
+		 * anymore. So what to do now?
+		 */
+	}
+}
+
+/*******************************************************************************
+ * Function : sata_dwc_qc_prep
+ * arguments : ata_queued_cmd *qc
+ * Return value : None
+ * qc_prep for a particular queued command
+ ******************************************************************************/
+static void sata_dwc_qc_prep(struct ata_queued_cmd *qc)
+{
+	u32 sactive;
+	u8 tag = qc->tag;
+
+#ifdef DEBUG_NCQ
+	if (qc->tag > 0) {
+		dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n",
+			 __func__, tag, qc->ap->link.active_tag);
+	}
+#endif
+
+	if (qc->tf.protocol == ATA_PROT_NCQ) {
+		sactive = core_scr_read(SCR_ACTIVE);
+		sactive |= (0x00000001 << tag);
+		core_scr_write(SCR_ACTIVE, sactive);
+		dev_dbg(qc->ap->dev, "%s: tag=%d ap->link.sactive = 0x%08x "
+			"sactive=0x%08x\n", __func__, tag, qc->ap->link.sactive,
+			sactive);
+	} else {
+		tag = 0;
+	}
+
+	sata_dwc_qc_prep_by_tag(qc, tag);
+}
+
+/*
+ * test-only: Needed when no drive is connected upon driver startup.
+ * Otherwise, by using the default prereset routine, the driver crashes
+ * upon loading.
+ * Not sure if this could be handled differently.
+ */
+static int sata_dwc_prereset(struct ata_link *link, unsigned long deadline)
+{
+	struct ata_port *ap = link->ap;
+	struct ata_eh_context *ehc = &ap->link.eh_context;
+	int rc = 0;
+
+	if (ata_link_online(link)) {
+		rc = ata_sff_wait_ready(link, deadline);
+	} else {
+		/* tell EH to bail */
+		ehc->i.action &= ~ATA_EH_RESET;
+	}
+
+	return rc;
+}
+
+/*
+ * scsi mid-layer and libata interface structures
+ */
+static struct scsi_host_template sata_dwc_sht = {
+	ATA_NCQ_SHT(DRV_NAME),
+	/*
+	 * test-only: Currently this driver doesn't handle NCQ
+	 * correctly. We enable NCQ but set the queue depth to a
+	 * max of 1. This will get fixed in in a future release.
+	 */
+	.can_queue = ATA_DEF_QUEUE,	/* ATA_MAX_QUEUE */
+	.dma_boundary = ATA_DMA_BOUNDARY,
+};
+
+static struct ata_port_operations sata_dwc_ops = {
+	.inherits		= &ata_sff_port_ops,
+
+	.qc_prep		= sata_dwc_qc_prep,
+	.qc_issue		= sata_dwc_qc_issue,
+
+	.scr_read		= sata_dwc_scr_read,
+	.scr_write		= sata_dwc_scr_write,
+
+	.port_start		= sata_dwc_port_start,
+	.port_stop		= sata_dwc_port_stop,
+
+	.bmdma_setup		= sata_dwc_bmdma_setup,
+	.bmdma_start		= sata_dwc_bmdma_start,
+
+	.prereset		= sata_dwc_prereset,
+
+	/* test-only: really needed? */
+	.eng_timeout		= sata_dwc_eng_timeout,
+};
+
+static const struct ata_port_info sata_dwc_port_info[] = {
+	{
+		/*
+		 * test-only: Currently this driver doesn't handle NCQ
+		 * correctly. So we disable NCQ here for now. To enable
+		 * it ATA_FLAG_NCQ needs to be added to the flags below.
+		 */
+		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_MMIO,
+		.pio_mask	= 0x1f,	/* pio 0-4 */
+		.udma_mask	= ATA_UDMA6,
+		.port_ops	= &sata_dwc_ops,
+	},
+};
+
+static int sata_dwc_probe(struct platform_device *pdev)
+{
+	struct sata_dwc_device *hsdev;
+	u32 idr, versionr;
+	char *ver = (char *)&versionr;
+	u8 *base = NULL;
+	int err = 0;
+	struct resource *res;
+	int irq;
+	struct ata_host *host;
+	struct ata_port_info pi = sata_dwc_port_info[0];
+	const struct ata_port_info *ppi[] = { &pi, NULL };
+
+
+	/* Allocate DWC SATA device */
+	hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
+	if (hsdev == NULL) {
+		dev_err(&pdev->dev, "kmalloc failed for hsdev\n");
+		err = -ENOMEM;
+		goto error_out;
+	}
+	memset(hsdev, 0, sizeof(*hsdev));
+
+	/* Get physical SATA register base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no reg addr\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	/* Ioremap SATA registers */
+	base = ioremap(res->start, res->end - res->start + 1);
+	if (!base) {
+		dev_err(&pdev->dev, "ioremap failed for 0x%llx\n", res->start);
+		err = -ENODEV;
+		goto error_out;
+	}
+	hsdev->reg_base = base;
+	dev_dbg(&pdev->dev, "ioremap done (0x%llx -> 0x%08x)\n",
+			res->start, (u32)base);
+
+	/* Synopsys DWC SATA specific Registers */
+	hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET);
+
+	/* Allocate and fill host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, SATA_DWC_MAX_PORTS);
+	if (!host) {
+		dev_err(&pdev->dev, "ata_host_alloc_pinfo failed\n");
+		err = -ENOMEM;
+		goto error_out;
+	}
+
+	host->private_data = hsdev;
+
+	/* Setup port */
+	host->ports[0]->ioaddr.cmd_addr = base;
+	host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET;
+	scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET;
+	sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base);
+
+	/* Read the ID and Version Registers */
+	idr = in_le32(&hsdev->sata_dwc_regs->idr);
+	versionr = in_le32(&hsdev->sata_dwc_regs->versionr);
+	dev_notice(&pdev->dev, "id %d, controller version %c.%c%c\n",
+		   idr, ver[0], ver[1], ver[2]);
+
+	/* Get SATA DMA interrupt number */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no SATA DMA irq\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+	irq = res->start;
+
+	/* Get physical SATA DMA register base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no reg addr\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+
+	/* Save dev for later use in dev_xxx() routines */
+	dwc_dev = &pdev->dev;
+
+	/* Initialize AHB DMAC */
+	dma_dwc_init(hsdev, res, irq);
+
+	/* Enable SATA Interrupts */
+	sata_dwc_enable_interrupts(hsdev);
+
+	/* Get SATA interrupt number */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no SATA irq\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+	irq = res->start;
+
+	/*
+	 * Now, register with libATA core, this will also initiate the
+	 * device discovery process, invoking our port_start() handler &
+	 * error_handler() to execute a dummy Softreset EH session
+	 */
+	ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht);
+
+	dev_set_drvdata(&pdev->dev, host);
+
+	/* Everything is fine */
+	return 0;
+
+error_out:
+	/* Free SATA DMA resources */
+	dma_dwc_exit(hsdev);
+
+	if (base)
+		iounmap(base);
+
+	kfree(hsdev);
+
+	return err;
+}
+
+static int sata_dwc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct sata_dwc_device *hsdev = host->private_data;
+
+	ata_host_detach(host);
+
+	dev_set_drvdata(dev, NULL);
+
+	/* Free SATA DMA resources */
+	dma_dwc_exit(hsdev);
+
+	iounmap(hsdev->reg_base);
+	kfree(hsdev);
+	kfree(host);
+
+	dev_dbg(&pdev->dev, "done\n");
+
+	return 0;
+}
+
+static struct platform_driver sata_dwc_driver = {
+	.probe = sata_dwc_probe,
+	.remove = sata_dwc_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init sata_dwc_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&sata_dwc_driver);
+
+	return err;
+}
+
+static void __exit sata_dwc_exit(void)
+{
+	platform_driver_unregister(&sata_dwc_driver);
+}
+
+module_init(sata_dwc_init);
+module_exit(sata_dwc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>");
+MODULE_DESCRIPTION("DesignWare Cores SATA controller low lever driver");
+MODULE_VERSION(DRV_VERSION);
-- 
1.5.5

^ permalink raw reply related

* [RFC][PATCH] powerpc/pci: Pull ppc32 code we need in ppc64
From: Kumar Gala @ 2009-04-29 20:49 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---

Ben,

My question is if we think fake_pci_bus will always get a valid hose().
The users of EARLY_PCI_OP are fsl/8xxx, 4xx, and pmac.  I verified that
fsl/8xxx & 4xx pass a valid hose.  Do we think pmac does?

- k

 arch/powerpc/include/asm/machdep.h    |    6 +-
 arch/powerpc/include/asm/pci-bridge.h |   35 +++++++---------
 arch/powerpc/kernel/pci-common.c      |   70 ++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/pci_32.c          |   71 ---------------------------------
 4 files changed, 89 insertions(+), 93 deletions(-)

diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 0efdb1d..1b389ff 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -205,14 +205,14 @@ struct machdep_calls {
 	/*
 	 * optional PCI "hooks"
 	 */
-	/* Called in indirect_* to avoid touching devices */
-	int (*pci_exclude_device)(struct pci_controller *, unsigned char, unsigned char);
-
 	/* Called at then very end of pcibios_init() */
 	void (*pcibios_after_init)(void);

 #endif /* CONFIG_PPC32 */

+	/* Called in indirect_* to avoid touching devices */
+	int (*pci_exclude_device)(struct pci_controller *, unsigned char, unsigned char);
+
 	/* Called after PPC generic resource fixup to perform
 	   machine specific fixups */
 	void (*pcibios_fixup_resources)(struct pci_dev *);
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 48f58aa..8b371df 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -77,9 +77,7 @@ struct pci_controller {

 	int first_busno;
 	int last_busno;
-#ifndef CONFIG_PPC64
 	int self_busno;
-#endif

 	void __iomem *io_base_virt;
 #ifdef CONFIG_PPC64
@@ -104,7 +102,6 @@ struct pci_controller {
 	unsigned int __iomem *cfg_addr;
 	void __iomem *cfg_data;

-#ifndef CONFIG_PPC64
 	/*
 	 * Used for variants of PCI indirect handling and possible quirks:
 	 *  SET_CFG_TYPE - used on 4xx or any PHB that does explicit type0/1
@@ -128,7 +125,6 @@ struct pci_controller {
 #define PPC_INDIRECT_TYPE_BIG_ENDIAN		0x00000010
 #define PPC_INDIRECT_TYPE_BROKEN_MRM		0x00000020
 	u32 indirect_type;
-#endif	/* !CONFIG_PPC64 */
 	/* Currently, we limit ourselves to 1 IO range and 3 mem
 	 * ranges since the common pci_bus structure can't handle more
 	 */
@@ -146,21 +142,6 @@ struct pci_controller {
 #endif	/* CONFIG_PPC64 */
 };

-#ifndef CONFIG_PPC64
-
-static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
-{
-	return bus->sysdata;
-}
-
-static inline int isa_vaddr_is_ioport(void __iomem *address)
-{
-	/* No specific ISA handling on ppc32 at this stage, it
-	 * all goes through PCI
-	 */
-	return 0;
-}
-
 /* These are used for config access before all the PCI probing
    has been done. */
 extern int early_read_config_byte(struct pci_controller *hose, int bus,
@@ -182,6 +163,22 @@ extern int early_find_capability(struct pci_controller *hose, int bus,
 extern void setup_indirect_pci(struct pci_controller* hose,
 			       resource_size_t cfg_addr,
 			       resource_size_t cfg_data, u32 flags);
+
+#ifndef CONFIG_PPC64
+
+static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
+{
+	return bus->sysdata;
+}
+
+static inline int isa_vaddr_is_ioport(void __iomem *address)
+{
+	/* No specific ISA handling on ppc32 at this stage, it
+	 * all goes through PCI
+	 */
+	return 0;
+}
+
 #else	/* CONFIG_PPC64 */

 /*
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 9c69e7e..cae4ee1 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1620,3 +1620,73 @@ void __devinit pcibios_setup_phb_resources(struct pci_controller *hose)

 }

+/*
+ * Null PCI config access functions, for the case when we can't
+ * find a hose.
+ */
+#define NULL_PCI_OP(rw, size, type)					\
+static int								\
+null_##rw##_config_##size(struct pci_dev *dev, int offset, type val)	\
+{									\
+	return PCIBIOS_DEVICE_NOT_FOUND;    				\
+}
+
+static int
+null_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
+		 int len, u32 *val)
+{
+	return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+static int
+null_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
+		  int len, u32 val)
+{
+	return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+static struct pci_ops null_pci_ops =
+{
+	.read = null_read_config,
+	.write = null_write_config,
+};
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_bus *
+fake_pci_bus(struct pci_controller *hose, int busnr)
+{
+	static struct pci_bus bus;
+
+	if (hose == 0) {
+		printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr);
+	}
+	bus.number = busnr;
+	bus.sysdata = hose;
+	bus.ops = hose? hose->ops: &null_pci_ops;
+	return &bus;
+}
+
+#define EARLY_PCI_OP(rw, size, type)					\
+int early_##rw##_config_##size(struct pci_controller *hose, int bus,	\
+			       int devfn, int offset, type value)	\
+{									\
+	return pci_bus_##rw##_config_##size(fake_pci_bus(hose, bus),	\
+					    devfn, offset, value);	\
+}
+
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
+
+extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int cap);
+int early_find_capability(struct pci_controller *hose, int bus, int devfn,
+			  int cap)
+{
+	return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap);
+}
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index d473634..32fbadb 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -497,75 +497,4 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
 	return result;
 }

-/*
- * Null PCI config access functions, for the case when we can't
- * find a hose.
- */
-#define NULL_PCI_OP(rw, size, type)					\
-static int								\
-null_##rw##_config_##size(struct pci_dev *dev, int offset, type val)	\
-{									\
-	return PCIBIOS_DEVICE_NOT_FOUND;    				\
-}
-
-static int
-null_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
-		 int len, u32 *val)
-{
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-static int
-null_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
-		  int len, u32 val)
-{
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-static struct pci_ops null_pci_ops =
-{
-	.read = null_read_config,
-	.write = null_write_config,
-};

-/*
- * These functions are used early on before PCI scanning is done
- * and all of the pci_dev and pci_bus structures have been created.
- */
-static struct pci_bus *
-fake_pci_bus(struct pci_controller *hose, int busnr)
-{
-	static struct pci_bus bus;
-
-	if (hose == 0) {
-		hose = pci_bus_to_hose(busnr);
-		if (hose == 0)
-			printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr);
-	}
-	bus.number = busnr;
-	bus.sysdata = hose;
-	bus.ops = hose? hose->ops: &null_pci_ops;
-	return &bus;
-}
-
-#define EARLY_PCI_OP(rw, size, type)					\
-int early_##rw##_config_##size(struct pci_controller *hose, int bus,	\
-			       int devfn, int offset, type value)	\
-{									\
-	return pci_bus_##rw##_config_##size(fake_pci_bus(hose, bus),	\
-					    devfn, offset, value);	\
-}
-
-EARLY_PCI_OP(read, byte, u8 *)
-EARLY_PCI_OP(read, word, u16 *)
-EARLY_PCI_OP(read, dword, u32 *)
-EARLY_PCI_OP(write, byte, u8)
-EARLY_PCI_OP(write, word, u16)
-EARLY_PCI_OP(write, dword, u32)
-
-extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int cap);
-int early_find_capability(struct pci_controller *hose, int bus, int devfn,
-			  int cap)
-{
-	return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap);
-}
-- 
1.6.0.6

^ permalink raw reply related

* [PATCH] powerpc adjust oprofile_cpu_type version 3  resend
From: Mike Wolf @ 2009-04-29 20:47 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: paulus

Oprofile is changing the naming it is using for the compatibility modes.
Instead of having compat-power<x>, oprofile will go to family naming
convention and use ibm-compat-v<x>.  Currently only ibm-compat-v1 will
be defined.
The notion of compatibility events just started with POWER6. So there is
no way that any other tool could exist that is using these
oprofile_cpu_type strings we want to change.

Signed-off-by: Mike Wolf <mjw@linux.vnet.ibm.com>

----
--- mainline.orig/arch/powerpc/kernel/cputable.c	2009-04-16 09:47:49.000000000 -0500
+++ mainline/arch/powerpc/kernel/cputable.c	2009-04-27 10:28:03.000000000 -0500
@@ -382,7 +382,8 @@
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
 		.machine_check		= machine_check_generic,
-		.oprofile_cpu_type	= "ppc64/compat-power5+",
+		.oprofile_cpu_type	= "ppc64/ibm-compat-v1",
+		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.platform		= "power5+",
 	},
 	{	/* Power6 */
@@ -416,7 +417,8 @@
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
 		.machine_check		= machine_check_generic,
-		.oprofile_cpu_type	= "ppc64/compat-power6",
+		.oprofile_cpu_type	= "ppc64/ibm-compat-v1",
+		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.platform		= "power6",
 	},
 	{	/* 2.06-compliant processor, i.e. Power7 "architected" mode */
@@ -429,7 +431,8 @@
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
 		.machine_check		= machine_check_generic,
-		.oprofile_cpu_type	= "ppc64/compat-power7",
+		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.oprofile_cpu_type	= "ppc64/ibm-compat-v1",
 		.platform		= "power7",
 	},
 	{	/* Power7 */
@@ -1833,8 +1836,10 @@
 		 * and, in that case, keep the current value for
 		 * oprofile_cpu_type.
 		 */
-		if (old.oprofile_cpu_type == NULL)
+		if (old.oprofile_cpu_type == NULL) {
 			t->oprofile_cpu_type = s->oprofile_cpu_type;
+			t->oprofile_type = s->oprofile_type;
+		}
 	}
 
 	*PTRRELOC(&cur_cpu_spec) = &the_cpu_spec;

^ permalink raw reply

* Re: [PATCH 5/6] net/ucc_geth: Assign six threads to Rx for UEC
From: Kumar Gala @ 2009-04-29 20:25 UTC (permalink / raw)
  To: Haiying Wang; +Cc: linuxppc-dev
In-Reply-To: <1241028884406-git-send-email-Haiying.Wang@freescale.com>


On Apr 29, 2009, at 1:14 PM, Haiying Wang wrote:

> when there are 46 snums defined in device tree.
>
> Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com>
> ---
> drivers/net/ucc_geth.c |    5 ++++-
> 1 files changed, 4 insertions(+), 1 deletions(-)

Can you be a bit more explicit in the commit message about the  
detection of 46 snums.  How is it that 46 snums equates to 6 threads?

- k

>
>
> diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
> index 3e003fe..1f6529f 100644
> --- a/drivers/net/ucc_geth.c
> +++ b/drivers/net/ucc_geth.c
> @@ -3709,7 +3709,10 @@ static int ucc_geth_probe(struct of_device*  
> ofdev, const struct of_device_id *ma
> 		ug_info->uf_info.utfet = UCC_GETH_UTFET_GIGA_INIT;
> 		ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT;
> 		ug_info->numThreadsTx = UCC_GETH_NUM_OF_THREADS_4;
> -		ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_4;
> +		if (qe_get_num_of_snums() == 46)
> +			ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_6;
> +		else
> +			ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_4;
> 	}
>
> 	if (netif_msg_probe(&debug))
> -- 
> 1.6.0.2

^ permalink raw reply

* Re: [PATCH 3/6] net/ucc_geth: update riscTx and riscRx in ucc_geth
From: Kumar Gala @ 2009-04-29 20:23 UTC (permalink / raw)
  To: David Miller; +Cc: Linuxppc-dev Development, Netdev, Haiying Wang
In-Reply-To: <12410288811793-git-send-email-Haiying.Wang@freescale.com>


On Apr 29, 2009, at 1:14 PM, Haiying Wang wrote:

> Change the definition of riscTx and riscRx to unsigned integer  
> instead of enum,
> and change their values to support 4 risc allocation if the qe has 4  
> RISC
> engines.
>
> Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com>
> ---

Dave, if you can ack this it is probably easier if this goes via the  
powerpc tree as it had some dependancies.

- k

>
> drivers/net/ucc_geth.c |   14 +++++++++++---
> drivers/net/ucc_geth.h |    4 ++--
> 2 files changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
> index d3f39e8..3e003fe 100644
> --- a/drivers/net/ucc_geth.c
> +++ b/drivers/net/ucc_geth.c
> @@ -270,7 +270,7 @@ static int fill_init_enet_entries(struct  
> ucc_geth_private *ugeth,
> 				  u8 num_entries,
> 				  u32 thread_size,
> 				  u32 thread_alignment,
> -				  enum qe_risc_allocation risc,
> +				  unsigned int risc,
> 				  int skip_page_for_first_entry)
> {
> 	u32 init_enet_offset;
> @@ -307,7 +307,7 @@ static int fill_init_enet_entries(struct  
> ucc_geth_private *ugeth,
> static int return_init_enet_entries(struct ucc_geth_private *ugeth,
> 				    u32 *p_start,
> 				    u8 num_entries,
> -				    enum qe_risc_allocation risc,
> +				    unsigned int risc,
> 				    int skip_page_for_first_entry)
> {
> 	u32 init_enet_offset;
> @@ -342,7 +342,7 @@ static int dump_init_enet_entries(struct  
> ucc_geth_private *ugeth,
> 				  u32 __iomem *p_start,
> 				  u8 num_entries,
> 				  u32 thread_size,
> -				  enum qe_risc_allocation risc,
> +				  unsigned int risc,
> 				  int skip_page_for_first_entry)
> {
> 	u32 init_enet_offset;
> @@ -2134,6 +2134,14 @@ static int ucc_struct_init(struct  
> ucc_geth_private *ugeth)
> 		return -ENOMEM;
> 	}
>
> +	/* read the number of risc engines, update the riscTx and riscRx
> +	 * if there are 4 riscs in QE
> +	 */
> +	if (qe_get_num_of_risc() == 4) {
> +		ug_info->riscTx = QE_RISC_ALLOCATION_FOUR_RISCS;
> +		ug_info->riscRx = QE_RISC_ALLOCATION_FOUR_RISCS;
> +	}
> +
> 	ugeth->ug_regs = ioremap(uf_info->regs, sizeof(*ugeth->ug_regs));
> 	if (!ugeth->ug_regs) {
> 		if (netif_msg_probe(ugeth))
> diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h
> index 2f8ee7c..46bb1d2 100644
> --- a/drivers/net/ucc_geth.h
> +++ b/drivers/net/ucc_geth.h
> @@ -1120,8 +1120,8 @@ struct ucc_geth_info {
> 	enum ucc_geth_maccfg2_pad_and_crc_mode padAndCrc;
> 	enum ucc_geth_num_of_threads numThreadsTx;
> 	enum ucc_geth_num_of_threads numThreadsRx;
> -	enum qe_risc_allocation riscTx;
> -	enum qe_risc_allocation riscRx;
> +	unsigned int riscTx;
> +	unsigned int riscRx;
> };
>
> /* structure representing UCC GETH */
> -- 
> 1.6.0.2

^ permalink raw reply

* Re: [PATCH 1/6] powerpc: Move #ifdef'ed body of do_IRQ() into a separate function
From: Benjamin Herrenschmidt @ 2009-04-29 19:58 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev Development, Christoph Hellwig
In-Reply-To: <C9C597A9-3096-4BF1-8559-15FADFB2AEAD@kernel.crashing.org>

On Wed, 2009-04-29 at 07:48 -0500, Kumar Gala wrote:
> 
> I think Ben, Paul and I had discussed just universally enabling it.   
> Can't remember why Ben hadn't done that yet.

Slipped between the cracks. Patch welcome.

Cheers,
Ben.

^ permalink raw reply

* [PATCH 3/6] net/ucc_geth: update riscTx and riscRx in ucc_geth
From: Haiying Wang @ 2009-04-29 18:14 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Haiying Wang
In-Reply-To: <12410288792524-git-send-email-Haiying.Wang@freescale.com>

Change the definition of riscTx and riscRx to unsigned integer instead of enum,
and change their values to support 4 risc allocation if the qe has 4 RISC
engines.

Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com>
---
 drivers/net/ucc_geth.c |   14 +++++++++++---
 drivers/net/ucc_geth.h |    4 ++--
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index d3f39e8..3e003fe 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -270,7 +270,7 @@ static int fill_init_enet_entries(struct ucc_geth_private *ugeth,
 				  u8 num_entries,
 				  u32 thread_size,
 				  u32 thread_alignment,
-				  enum qe_risc_allocation risc,
+				  unsigned int risc,
 				  int skip_page_for_first_entry)
 {
 	u32 init_enet_offset;
@@ -307,7 +307,7 @@ static int fill_init_enet_entries(struct ucc_geth_private *ugeth,
 static int return_init_enet_entries(struct ucc_geth_private *ugeth,
 				    u32 *p_start,
 				    u8 num_entries,
-				    enum qe_risc_allocation risc,
+				    unsigned int risc,
 				    int skip_page_for_first_entry)
 {
 	u32 init_enet_offset;
@@ -342,7 +342,7 @@ static int dump_init_enet_entries(struct ucc_geth_private *ugeth,
 				  u32 __iomem *p_start,
 				  u8 num_entries,
 				  u32 thread_size,
-				  enum qe_risc_allocation risc,
+				  unsigned int risc,
 				  int skip_page_for_first_entry)
 {
 	u32 init_enet_offset;
@@ -2134,6 +2134,14 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
 		return -ENOMEM;
 	}
 
+	/* read the number of risc engines, update the riscTx and riscRx
+	 * if there are 4 riscs in QE
+	 */
+	if (qe_get_num_of_risc() == 4) {
+		ug_info->riscTx = QE_RISC_ALLOCATION_FOUR_RISCS;
+		ug_info->riscRx = QE_RISC_ALLOCATION_FOUR_RISCS;
+	}
+
 	ugeth->ug_regs = ioremap(uf_info->regs, sizeof(*ugeth->ug_regs));
 	if (!ugeth->ug_regs) {
 		if (netif_msg_probe(ugeth))
diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h
index 2f8ee7c..46bb1d2 100644
--- a/drivers/net/ucc_geth.h
+++ b/drivers/net/ucc_geth.h
@@ -1120,8 +1120,8 @@ struct ucc_geth_info {
 	enum ucc_geth_maccfg2_pad_and_crc_mode padAndCrc;
 	enum ucc_geth_num_of_threads numThreadsTx;
 	enum ucc_geth_num_of_threads numThreadsRx;
-	enum qe_risc_allocation riscTx;
-	enum qe_risc_allocation riscRx;
+	unsigned int riscTx;
+	unsigned int riscRx;
 };
 
 /* structure representing UCC GETH */
-- 
1.6.0.2

^ permalink raw reply related

* [PATCH 2/6] powerpc/qe: update risc allocation for QE
From: Haiying Wang @ 2009-04-29 18:14 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Haiying Wang
In-Reply-To: <1241028878175-git-send-email-Haiying.Wang@freescale.com>

Change the RISC allocation to macros instead of enum, add function to read the
number of risc engines from the new property "num-riscs" under qe node in dts.
Add new property "num-riscs" description in qe.txt

Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com>
---
 .../powerpc/dts-bindings/fsl/cpm_qe/qe.txt         |    1 +
 arch/powerpc/include/asm/qe.h                      |   18 ++++++++----
 arch/powerpc/sysdev/qe_lib/qe.c                    |   28 ++++++++++++++++++++
 3 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe.txt b/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe.txt
index 78790d5..b6ea613 100644
--- a/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe.txt
@@ -21,6 +21,7 @@ Required properties:
 Recommended properties
 - brg-frequency : the internal clock source frequency for baud-rate
   generators in Hz.
+- num-riscs: define how many RISC engines the QE has.
 
 Example:
      qe@e0100000 {
diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h
index 2701753..60314ef 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/arch/powerpc/include/asm/qe.h
@@ -152,6 +152,8 @@ unsigned int qe_get_brg_clk(void);
 int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier);
 int qe_get_snum(void);
 void qe_put_snum(u8 snum);
+unsigned int qe_get_num_of_risc(void);
+
 /* we actually use cpm_muram implementation, define this for convenience */
 #define qe_muram_init cpm_muram_init
 #define qe_muram_alloc cpm_muram_alloc
@@ -231,12 +233,16 @@ struct qe_bd {
 #define QE_ALIGNMENT_OF_PRAM	64
 
 /* RISC allocation */
-enum qe_risc_allocation {
-	QE_RISC_ALLOCATION_RISC1 = 1,	/* RISC 1 */
-	QE_RISC_ALLOCATION_RISC2 = 2,	/* RISC 2 */
-	QE_RISC_ALLOCATION_RISC1_AND_RISC2 = 3	/* Dynamically choose
-						   RISC 1 or RISC 2 */
-};
+#define QE_RISC_ALLOCATION_RISC1	0x1  /* RISC 1 */
+#define QE_RISC_ALLOCATION_RISC2	0x2  /* RISC 2 */
+#define QE_RISC_ALLOCATION_RISC3	0x4  /* RISC 3 */
+#define QE_RISC_ALLOCATION_RISC4	0x8  /* RISC 4 */
+#define QE_RISC_ALLOCATION_RISC1_AND_RISC2	(QE_RISC_ALLOCATION_RISC1 | \
+						 QE_RISC_ALLOCATION_RISC2)
+#define QE_RISC_ALLOCATION_FOUR_RISCS	(QE_RISC_ALLOCATION_RISC1 | \
+					 QE_RISC_ALLOCATION_RISC2 | \
+					 QE_RISC_ALLOCATION_RISC3 | \
+					 QE_RISC_ALLOCATION_RISC4)
 
 /* QE extended filtering Table Lookup Key Size */
 enum qe_fltr_tbl_lookup_key_size {
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 01bce37..31a3bb1 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -575,3 +575,31 @@ struct qe_firmware_info *qe_get_firmware_info(void)
 }
 EXPORT_SYMBOL(qe_get_firmware_info);
 
+unsigned int qe_get_num_of_risc(void)
+{
+	struct device_node *qe;
+	int size;
+	unsigned int num_of_risc = 0;
+	const u32 *prop;
+
+	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
+	if (!qe) {
+		/* Older devices trees did not have an "fsl,qe"
+		 * compatible property, so we need to look for
+		 * the QE node by name.
+		 */
+		qe = of_find_node_by_type(NULL, "qe");
+		if (!qe)
+			return num_of_risc;
+	}
+
+	prop = of_get_property(qe, "num-riscs", &size);
+	if (prop && size == sizeof(*prop))
+		num_of_risc = *prop;
+
+	of_node_put(qe);
+
+	return num_of_risc;
+}
+EXPORT_SYMBOL(qe_get_num_of_risc);
+
-- 
1.6.0.2

^ permalink raw reply related


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