From kumba@gentoo.org Thu Mar  1 04:34:20 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 04:34:25 +0000 (GMT)
Received: from rwcrmhc14.comcast.net ([204.127.192.84]:12252 "EHLO
	rwcrmhc14.comcast.net") by ftp.linux-mips.org with ESMTP
	id S20037750AbXCAEeU (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 04:34:20 +0000
Received: from [192.168.1.4] (c-68-34-70-207.hsd1.md.comcast.net[68.34.70.207])
          by comcast.net (rwcrmhc14) with ESMTP
          id <20070301043335m1400hmpkre>; Thu, 1 Mar 2007 04:33:35 +0000
Message-ID: <45E6578F.9060407@gentoo.org>
Date:	Wed, 28 Feb 2007 23:33:19 -0500
From:	Kumba <kumba@gentoo.org>
User-Agent: Thunderbird 2.0b2 (Windows/20070116)
MIME-Version: 1.0
To:	Linux MIPS List <linux-mips@linux-mips.org>
Subject: Re: IP32 prom crashes due to __pa() funkiness
References: <45D8B070.7070405@gentoo.org>
In-Reply-To: <45D8B070.7070405@gentoo.org>
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Return-Path: <kumba@gentoo.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14293
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: kumba@gentoo.org
Precedence: bulk
X-list: linux-mips

Kumba wrote:
> 
> Initially, booting a straight git checkout on an IP32 will cause it to 
> prom crash, usually somewhere in between init_bootmem() and 
> init_bootmem_core().  I bisected git to trace this back to one of the 
> inital __pa() introduction patches from commit d4df6d4 (get ride of 
> CPHYSADDR()).  It actually appears that the actual commit that broke 
> things was 620a480 (Make __pa() aware of XKPHYS/CKSEG0 address mix for 
> 64 bit kernels).
> 
> The [short-term] fix highlighted by Ilya is to make __pa() 
> unconditionally be defined to "((unsigned long)(x) < CKSEG0 ? 
> PAGE_OFFSET : CKSEG0)"; Discovered by building IP32 with 
> CONFIG_BUILD_ELF64=n.
> 
> Normally, this shouldn't be possible, as CONFIG_BUILD_ELF64=n was 
> originally only allowed by using the old o64 hack, which has 
> subsequently died and been replaced with the newer -msym32 form.  As far 
> as I know, CONFIG_BUILD_ELF64 is apparently supposed to be removed at 
> some point in the future, since I believe it existed only for quirky 
> 64bit-in-32bit kernels for IP22 and more commonly, IP32.  From 2.6.17 
> onwards, I've been building IP32 kernels with CONFIG_BUILD_ELF64=y and 
> using gcc-4, and haven't had problems up until now.
> 
> Anyways, I'm not sure if this is an IP32-specific oddity or not 
> (probably is), but it needs the define highlighted above to work 
> properly.  Plain PAGE_OFFSET won't work for these machines.  Given the 
> same trick os -msym32 is used for the rare IP22 64bit kernel, I would 
> not be surprised if the same problem and fix both occur and work for 
> those machines as well.  Something to maybe test later, I suppose.
> 
> But for now, anyone got thoughts as to a sane workaround for this?  
> Perhaps some conditional tweaks in mach-ip32/*.h files somewheres, would 
> it be simpler to just switch to:
> 
> #if defined(CONFIG_64BIT) && (!defined(CONFIG_BUILD_ELF64) || 
> defined(CONFIG_SGI_IP32))
> 
> (assuming that IP22 doesn't need it; I'll find out later)

"Re-verify range to target, One ping only."


So, anyone got thoughts on this?

I've actually built and booted an SGI IP32 kernel using this logic in 
include/asm-mips/page.h, Line 135:

#if defined(CONFIG_64BIT) && (!defined(CONFIG_BUILD_ELF64) || 
(defined(CONFIG_SGI_IP32) || defined(CONFIG_SGI_IP22)))



--Kumba

-- 
Gentoo/MIPS Team Lead

"Such is oft the course of deeds that move the wheels of the world: small hands 
do them because they must, while the eyes of the great are elsewhere."  --Elrond

From riamae@gmail.com Thu Mar  1 04:34:49 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 04:34:53 +0000 (GMT)
Received: from nf-out-0910.google.com ([64.233.182.187]:49797 "EHLO
	nf-out-0910.google.com") by ftp.linux-mips.org with ESMTP
	id S20038845AbXCAEea (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 04:34:30 +0000
Received: by nf-out-0910.google.com with SMTP id l24so759371nfc
        for <linux-mips@linux-mips.org>; Wed, 28 Feb 2007 20:33:29 -0800 (PST)
DKIM-Signature:	a=rsa-sha1; c=relaxed/relaxed;
        d=gmail.com; s=beta;
        h=domainkey-signature:received:received:message-id:date:from:reply-to:to:subject:cc:in-reply-to:mime-version:content-type:references;
        b=hlns2RpgDa6MTh5+CfG44TH4djbtFn8B0ECJEal2xlb1+KeMtxTcOFaQEssIkBEulQc5hV8lz7ZB0rexBAgke0BkXC9NwxZMbbFjx1xhBDrygJDkL7bwUrruMjZizh91Bkdy/9TXYOMqqcFHvMxw3VA8GGwSyXKVw89BKfEUGIk=
DomainKey-Signature: a=rsa-sha1; c=nofws;
        d=gmail.com; s=beta;
        h=received:message-id:date:from:reply-to:to:subject:cc:in-reply-to:mime-version:content-type:references;
        b=hJIjKRI5hafqVUSt+648nFqeNBh8LJS8yTdzZnRjPI9pgQ9wvkRlubQ5K8MAGJk6wCfcPWgztczx9i+YQblpuQVMFwZ0SVZr1obIxoOvC0RMWcNF5uI66P0F+3iS8k/sZpBvWZx6zPPcHFnTO8l1loYY0pcRyieTIUkyE/Ywmwc=
Received: by 10.82.187.16 with SMTP id k16mr478891buf.1172723609104;
        Wed, 28 Feb 2007 20:33:29 -0800 (PST)
Received: by 10.82.152.14 with HTTP; Wed, 28 Feb 2007 20:33:29 -0800 (PST)
Message-ID: <d28769380702282033t13a918cdkd8baf084a2ea79a8@mail.gmail.com>
Date:	Thu, 1 Mar 2007 12:33:29 +0800
From:	dejo <riamae@gmail.com>
Reply-To: riamae@gmail.com
To:	"Ralf Baechle" <ralf@linux-mips.org>
Subject: Re: early_initcall replacement
Cc:	linux-mips@linux-mips.org
In-Reply-To: <20070228024756.GA23519@linux-mips.org>
MIME-Version: 1.0
Content-Type: multipart/alternative; 
	boundary="----=_Part_33195_47410.1172723609039"
References: <d28769380702271755u675241b3vb8b265120a2c70ca@mail.gmail.com>
	 <20070228024756.GA23519@linux-mips.org>
Return-Path: <riamae@gmail.com>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14294
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: riamae@gmail.com
Precedence: bulk
X-list: linux-mips

------=_Part_33195_47410.1172723609039
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Thank you for your reply, sir. I found out that there's already a
plat_mem_setup defined in vr41xx/common/init.c that's why I cannot rename
the function invoked by early_initcall to plat_mem_setup.

Actually, when I examined it further, I found out that using arch_initcall
works just fine except that the add_memory_region is not called before
everything else. It causes error because there's no memory region allotted.

I did this:
/*arch/mips/vr41xx/common/init.c */
void __init plat_mem_setup(void)
{
    vr41xx_calculate_clock_frequency();

    timer_init();
    iomem_resource_init();
            add_memory_region(0, 0x04000000, BOOT_MEM_RAM);
}

and it worked! But it's not good practice isn't it? I might ruin the other
vr41xx boards. Do you have any suggestions? Thank you.

ria mae


On 2/28/07, Ralf Baechle <ralf@linux-mips.org> wrote:
>
> On Wed, Feb 28, 2007 at 09:55:28AM +0800, dejo wrote:
>
> > Hello! I would like to ask what replaced early_initcall in the
> > 2.6.18.4kernel.
>
> What you need to change is
>
> o remove the early_initcall() line.
> o rename the function invoked by early_initcall to plat_mem_setup and
>    change its prototype to: "void __init plat_mem_setup(void)".
>
>   Ralf
>

------=_Part_33195_47410.1172723609039
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Thank you for your reply, sir. I found out that there&#39;s already a
plat_mem_setup defined in vr41xx/common/init.c that&#39;s why I cannot
rename the function invoked by early_initcall to plat_mem_setup.<br><br>Actually,
when I examined it further, I found out that using arch_initcall works
just fine except that the add_memory_region is not called before
everything else. It causes error because there&#39;s no memory region
allotted. <br><br>I did this:<br>/*arch/mips/vr41xx/common/init<div id="mb_2">.c */<span class="q"><br>void __init plat_mem_setup(void)<br></span>{<br>&nbsp;&nbsp;&nbsp; vr41xx_calculate_clock_frequency();<br><br>&nbsp;&nbsp;&nbsp; timer_init();<br>&nbsp;&nbsp;&nbsp; iomem_resource_init();
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add_memory_region(0, 0x04000000, BOOT_MEM_RAM);
<br>}<br><br>and it worked! But it&#39;s not good practice isn&#39;t it? I
might ruin the other vr41xx boards. Do you have any suggestions? Thank
you. <br><br>ria mae</div><br><br><div><span class="gmail_quote">On 2/28/07, <b class="gmail_sendername">Ralf Baechle</b> &lt;<a href="mailto:ralf@linux-mips.org">ralf@linux-mips.org</a>&gt; wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
On Wed, Feb 28, 2007 at 09:55:28AM +0800, dejo wrote:<br><br>&gt; Hello! I would like to ask what replaced early_initcall in the<br>&gt; 2.6.18.4kernel.<br><br>What you need to change is<br><br> o remove the early_initcall() line.
<br> o rename the function invoked by early_initcall to plat_mem_setup and<br>&nbsp;&nbsp; change its prototype to: &quot;void __init plat_mem_setup(void)&quot;.<br><br>&nbsp;&nbsp;Ralf<br></blockquote></div><br>

------=_Part_33195_47410.1172723609039--

From vagabon.xyz@gmail.com Thu Mar  1 09:40:20 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 09:40:25 +0000 (GMT)
Received: from qb-out-0506.google.com ([72.14.204.238]:64617 "EHLO
	qb-out-0506.google.com") by ftp.linux-mips.org with ESMTP
	id S20039230AbXCAJkU (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 09:40:20 +0000
Received: by qb-out-0506.google.com with SMTP id e12so331123qba
        for <linux-mips@linux-mips.org>; Thu, 01 Mar 2007 01:39:18 -0800 (PST)
DKIM-Signature:	a=rsa-sha1; c=relaxed/relaxed;
        d=gmail.com; s=beta;
        h=domainkey-signature:received:received:message-id:date:from:to:subject:cc:in-reply-to:mime-version:content-type:content-transfer-encoding:content-disposition:references;
        b=qxorT7Qdq/auhwfhrNAmnMiemK6JWTnRASDei26Lsa2pF3M3NBTDhE7lBLdFmUyksaRjPaGVNztmEMi+VQel7iBnAXC/xlNeAJPGDLRTv7xZ7+y2RXwWrdPC0P/5rHmVhfdsH57cUUc2DXeB1LpMUkXvMrc4JlqNe+NmwizyIq4=
DomainKey-Signature: a=rsa-sha1; c=nofws;
        d=gmail.com; s=beta;
        h=received:message-id:date:from:to:subject:cc:in-reply-to:mime-version:content-type:content-transfer-encoding:content-disposition:references;
        b=YtGWX43mxixUhBLw6pNj1dli56pys95vfDucLR27hvP9aaM4Tz9OBrQZHB8ellWeKS85azYSM3PX7W4xIYZY4qz5xVqy0hphsiYBbED8Lt1Ot4s8gQELl+vuMb2FdILl16Ivb/ARKE4vCsKz9JHpk/xCuQ7SeWZJp8DgQhzj5jI=
Received: by 10.114.13.1 with SMTP id 1mr86193wam.1172741948570;
        Thu, 01 Mar 2007 01:39:08 -0800 (PST)
Received: by 10.114.136.11 with HTTP; Thu, 1 Mar 2007 01:39:08 -0800 (PST)
Message-ID: <cda58cb80703010139y3e5bbb8eqa4d25b75ba658a22@mail.gmail.com>
Date:	Thu, 1 Mar 2007 10:39:08 +0100
From:	"Franck Bui-Huu" <vagabon.xyz@gmail.com>
To:	Kumba <kumba@gentoo.org>
Subject: Re: IP32 prom crashes due to __pa() funkiness
Cc:	"Linux MIPS List" <linux-mips@linux-mips.org>
In-Reply-To: <45D8B070.7070405@gentoo.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
References: <45D8B070.7070405@gentoo.org>
Return-Path: <vagabon.xyz@gmail.com>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14295
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: vagabon.xyz@gmail.com
Precedence: bulk
X-list: linux-mips

Hi,

Kumba wrote:
>
> Initially, booting a straight git checkout on an IP32 will cause it to
> prom crash, usually somewhere in between init_bootmem() and
> init_bootmem_core().  I bisected git to trace this back to one of the
> inital __pa() introduction patches from commit d4df6d4 (get ride of
> CPHYSADDR()).  It actually appears that the actual commit that broke
> things was 620a480 (Make __pa() aware of XKPHYS/CKSEG0 address mix for
> 64 bit kernels).
>
> The [short-term] fix highlighted by Ilya is to make __pa()
> unconditionally be defined to "((unsigned long)(x) < CKSEG0 ?
> PAGE_OFFSET : CKSEG0)"; Discovered by building IP32 with
> CONFIG_BUILD_ELF64=n.
>

Well, it means that you previously used CONFIG_BUILD_ELF64=y (this
implied that PAGE_OFFSET is in XKPHYS) whereas your kernel has CKSEG
load address (symbols need PAGE_OFFSET in CKSEG for address
translation).

So the question is why can't you use CONFIG_BUILD_ELF64=n (and
reagarding the current definition of CONFIG_BUILD_ELF64).

> Normally, this shouldn't be possible, as CONFIG_BUILD_ELF64=n was
> originally only allowed by using the old o64 hack, which has
> subsequently died and been replaced with the newer -msym32 form.  As far
> as I know, CONFIG_BUILD_ELF64 is apparently supposed to be removed at
> some point in the future, since I believe it existed only for quirky

It makes me think that I posted a patch for that a couple of weeks ago:

http://marc.theaimsgroup.com/?l=linux-mips&m=117154480225936&w=2
http://marc.theaimsgroup.com/?l=linux-mips&m=117154480126802&w=2
http://marc.theaimsgroup.com/?l=linux-mips&m=117154587014827&w=2

Basically this patch removes CONFIG_BUILD_ELF64 and makes Kbuild to use
'-msym32' switch if you really need it. Kbuild makes its choice according
the load address of your kernel image.

Could you give it a try ? This patch was based on 2.6.20 but it should
apply fine on a 2.6.21-rc[12].
-- 
               Franck

From yoichi_yuasa@tripeaks.co.jp Thu Mar  1 13:51:50 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 13:51:56 +0000 (GMT)
Received: from mo31.po.2iij.net ([210.128.50.54]:30499 "EHLO mo31.po.2iij.net")
	by ftp.linux-mips.org with ESMTP id S20039214AbXCANvu (ORCPT
	<rfc822;linux-mips@linux-mips.org>); Thu, 1 Mar 2007 13:51:50 +0000
Received: by mo.po.2iij.net (mo31) id l21DoTUQ067439; Thu, 1 Mar 2007 22:50:29 +0900 (JST)
Received: from localhost.localdomain (70.27.30.125.dy.iij4u.or.jp [125.30.27.70])
	by mbox.po.2iij.net (mbox32) id l21DoPRx060162
	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT);
	Thu, 1 Mar 2007 22:50:26 +0900 (JST)
Date:	Thu, 1 Mar 2007 22:50:25 +0900
From:	Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
To:	Ralf Baechle <ralf@linux-mips.org>
Cc:	yoichi_yuasa@tripeaks.co.jp, linux-mips <linux-mips@linux-mips.org>
Subject: [PATCH][MIPS] update Cobalt reserved resource
Message-Id: <20070301225025.7612e583.yoichi_yuasa@tripeaks.co.jp>
Organization: TriPeaks Corporation
X-Mailer: Sylpheed version 1.0.6 (GTK+ 1.2.10; i486-pc-linux-gnu)
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Return-Path: <yoichi_yuasa@tripeaks.co.jp>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14296
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: yoichi_yuasa@tripeaks.co.jp
Precedence: bulk
X-list: linux-mips

Hi Ralf,

This patch has removed unused timer resource.
Moreover, the name of reserved resources ware changed.

Yoichi

Signed-off-by: Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>

diff -pruN -X mips/Documentation/dontdiff mips-orig/arch/mips/cobalt/setup.c mips/arch/mips/cobalt/setup.c
--- mips-orig/arch/mips/cobalt/setup.c	2006-12-09 21:03:07.860183750 +0900
+++ mips/arch/mips/cobalt/setup.c	2006-12-09 21:44:28.215782000 +0900
@@ -79,37 +79,38 @@ static struct resource cobalt_io_resourc
 	.flags	= IORESOURCE_IO
 };
 
-static struct resource cobalt_io_resources[] = {
-	{
+/*
+ * Cobalt doesn't have PS/2 keyboard/mouse interfaces,
+ * keyboard conntroller is never used.
+ * Also PCI-ISA bridge DMA contoroller is never used.
+ */
+static struct resource cobalt_reserved_resources[] = {
+	{	/* dma1 */
 		.start	= 0x00,
 		.end	= 0x1f,
-		.name	= "dma1",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x40,
-		.end	= 0x5f,
-		.name	= "timer",
-		.flags	= IORESOURCE_BUSY
-	}, {
+		.name	= "reserved",
+		.flags	= IORESOURCE_BUSY | IORESOURCE_IO,
+	},
+	{	/* keyboard */
 		.start	= 0x60,
 		.end	= 0x6f,
-		.name	= "keyboard",
-		.flags	= IORESOURCE_BUSY
-	}, {
+		.name	= "reserved",
+		.flags	= IORESOURCE_BUSY | IORESOURCE_IO,
+	},
+	{	/* dma page reg */
 		.start	= 0x80,
 		.end	= 0x8f,
-		.name	= "dma page reg",
-		.flags	= IORESOURCE_BUSY
-	}, {
+		.name	= "reserved",
+		.flags	= IORESOURCE_BUSY | IORESOURCE_IO,
+	},
+	{	/* dma2 */
 		.start	= 0xc0,
 		.end	= 0xdf,
-		.name	= "dma2",
-		.flags	= IORESOURCE_BUSY
+		.name	= "reserved",
+		.flags	= IORESOURCE_BUSY | IORESOURCE_IO,
 	},
 };
 
-#define COBALT_IO_RESOURCES (sizeof(cobalt_io_resources)/sizeof(struct resource))
-
 static struct pci_controller cobalt_pci_controller = {
 	.pci_ops	= &gt64111_pci_ops,
 	.mem_resource	= &cobalt_mem_resource,
@@ -133,9 +134,9 @@ void __init plat_mem_setup(void)
 	/* I/O port resource must include LCD/buttons */
 	ioport_resource.end = 0x0fffffff;
 
-	/* request I/O space for devices used on all i[345]86 PCs */
-	for (i = 0; i < COBALT_IO_RESOURCES; i++)
-		request_resource(&ioport_resource, cobalt_io_resources + i);
+	/* These resources have been reserved by VIA SuperI/O chip. */
+	for (i = 0; i < ARRAY_SIZE(cobalt_reserved_resources); i++)
+		request_resource(&ioport_resource, cobalt_reserved_resources + i);
 
         /* Read the cobalt id register out of the PCI config space */
         PCI_CFG_SET(devfn, (VIA_COBALT_BRD_ID_REG & ~0x3));

From macro@linux-mips.org Thu Mar  1 13:57:22 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 13:57:27 +0000 (GMT)
Received: from pollux.ds.pg.gda.pl ([153.19.208.7]:45330 "EHLO
	pollux.ds.pg.gda.pl") by ftp.linux-mips.org with ESMTP
	id S20039427AbXCAN5W (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 13:57:22 +0000
Received: from localhost (localhost [127.0.0.1])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id 9DCE0E1CE0;
	Thu,  1 Mar 2007 14:56:37 +0100 (CET)
X-Virus-Scanned: by amavisd-new at pollux.ds.pg.gda.pl
Received: from pollux.ds.pg.gda.pl ([127.0.0.1])
	by localhost (pollux.ds.pg.gda.pl [127.0.0.1]) (amavisd-new, port 10024)
	with ESMTP id VsH9wdrS6dwq; Thu,  1 Mar 2007 14:56:37 +0100 (CET)
Received: from piorun.ds.pg.gda.pl (piorun.ds.pg.gda.pl [153.19.208.8])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id 244F9E1C69;
	Thu,  1 Mar 2007 14:56:37 +0100 (CET)
Received: from blysk.ds.pg.gda.pl (macro@blysk.ds.pg.gda.pl [153.19.208.6])
	by piorun.ds.pg.gda.pl (8.13.8/8.13.8) with ESMTP id l21DukqM011264;
	Thu, 1 Mar 2007 14:56:46 +0100
Date:	Thu, 1 Mar 2007 13:56:42 +0000 (GMT)
From:	"Maciej W. Rozycki" <macro@linux-mips.org>
To:	Andrew Morton <akpm@osdl.org>, Antonino Daplas <adaplas@pol.net>
cc:	linux-mips@linux-mips.org, linux-kernel@vger.kernel.org,
	linux-fbdev-devel@linux-mips.org
Subject: [PATCH 2.6.21-rc2 #3] tgafb: TURBOchannel support
Message-ID: <Pine.LNX.4.64N.0703011319480.25556@blysk.ds.pg.gda.pl>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
X-Virus-Scanned: ClamAV 0.90/2689/Thu Mar  1 06:46:09 2007 on piorun.ds.pg.gda.pl
X-Virus-Status:	Clean
Return-Path: <macro@linux-mips.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14297
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: macro@linux-mips.org
Precedence: bulk
X-list: linux-mips

 This is support for the TC variations of the TGA boards (properly known 
as SFB+ or Smart Frame Buffer Plus boards).  The 8-plane SFB+ board uses 
the Bt459 RAMDAC (unlike its PCI TGA counterpart, which uses the Bt485), 
so bits have been added to support this chip as well.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
---

 This is a new version which applies against 2.6.21-rc2.  It includes a 
small update to the help text in Kconfig as well.  No functional changes.

 Please apply.

  Maciej

patch-mips-2.6.21-rc1-20070222-hx+-73
diff -up --recursive --new-file linux-mips-2.6.21-rc1-20070222.macro/drivers/video/Kconfig linux-mips-2.6.21-rc1-20070222/drivers/video/Kconfig
--- linux-mips-2.6.21-rc1-20070222.macro/drivers/video/Kconfig	2007-02-22 06:01:02.000000000 +0000
+++ linux-mips-2.6.21-rc1-20070222/drivers/video/Kconfig	2007-03-01 00:43:32.000000000 +0000
@@ -516,15 +516,25 @@ config FB_HP300
 	default y
 
 config FB_TGA
-	tristate "TGA framebuffer support"
-	depends on FB && ALPHA
+	tristate "TGA/SFB+ framebuffer support"
+	depends on FB && (ALPHA || TC)
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
 	select BITREVERSE
-	help
-	  This is the frame buffer device driver for generic TGA graphic
-	  cards. Say Y if you have one of those.
+	---help---
+	  This is the frame buffer device driver for generic TGA and SFB+
+	  graphic cards.  These include DEC ZLXp-E1, -E2 and -E3 PCI cards,
+	  also known as PBXGA-A, -B and -C, and DEC ZLX-E1, -E2 and -E3
+	  TURBOchannel cards, also known as PMAGD-A, -B and -C.
+
+	  Due to hardware limitations ZLX-E2 and E3 cards are not supported
+	  for DECstation 5000/200 systems.  Additionally due to firmware
+	  limitations these cards may cause troubles with booting DECstation
+	  5000/240 and /260 systems, but are fully supported under Linux if
+	  you manage to get it going. ;-)
+
+	  Say Y if you have one of those.
 
 config FB_VESA
 	bool "VESA VGA graphics support"
diff -up --recursive --new-file linux-mips-2.6.21-rc1-20070222.macro/drivers/video/tgafb.c linux-mips-2.6.21-rc1-20070222/drivers/video/tgafb.c
--- linux-mips-2.6.21-rc1-20070222.macro/drivers/video/tgafb.c	2007-02-21 05:56:49.000000000 +0000
+++ linux-mips-2.6.21-rc1-20070222/drivers/video/tgafb.c	2007-03-01 00:32:46.000000000 +0000
@@ -5,27 +5,45 @@
  *	Copyright (C) 1997 Geert Uytterhoeven
  *	Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha
  *	Copyright (C) 2002 Richard Henderson
+ *	Copyright (C) 2006 Maciej W. Rozycki
  *
  *  This file is subject to the terms and conditions of the GNU General Public
  *  License. See the file COPYING in the main directory of this archive for
  *  more details.
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
+#include <linux/bitrev.h>
 #include <linux/delay.h>
-#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
 #include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/selection.h>
-#include <linux/bitrev.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tc.h>
+
 #include <asm/io.h>
+
 #include <video/tgafb.h>
 
+#ifdef CONFIG_PCI
+#define TGA_BUS_PCI(dev) (dev->bus == &pci_bus_type)
+#else
+#define TGA_BUS_PCI(dev) 0
+#endif
+
+#ifdef CONFIG_TC
+#define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type)
+#else
+#define TGA_BUS_TC(dev) 0
+#endif
+
 /*
  * Local functions.
  */
@@ -42,12 +60,16 @@ static void tgafb_imageblit(struct fb_in
 static void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *);
 static void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *);
 
-static int __devinit tgafb_pci_register(struct pci_dev *,
-					const struct pci_device_id *);
-static void __devexit tgafb_pci_unregister(struct pci_dev *);
+static int __devinit tgafb_register(struct device *dev);
+static void __devexit tgafb_unregister(struct device *dev);
+
+static const char *mode_option;
+static const char *mode_option_pci = "640x480@60";
+static const char *mode_option_tc = "1280x1024@72";
 
-static const char *mode_option = "640x480@60";
 
+static struct pci_driver tgafb_pci_driver;
+static struct tc_driver tgafb_tc_driver;
 
 /*
  *  Frame buffer operations
@@ -65,9 +87,13 @@ static struct fb_ops tgafb_ops = {
 };
 
 
+#ifdef CONFIG_PCI
 /*
  *  PCI registration operations
  */
+static int __devinit tgafb_pci_register(struct pci_dev *,
+					const struct pci_device_id *);
+static void __devexit tgafb_pci_unregister(struct pci_dev *);
 
 static struct pci_device_id const tgafb_pci_table[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
@@ -75,13 +101,68 @@ static struct pci_device_id const tgafb_
 };
 MODULE_DEVICE_TABLE(pci, tgafb_pci_table);
 
-static struct pci_driver tgafb_driver = {
+static struct pci_driver tgafb_pci_driver = {
 	.name			= "tgafb",
 	.id_table		= tgafb_pci_table,
 	.probe			= tgafb_pci_register,
 	.remove			= __devexit_p(tgafb_pci_unregister),
 };
 
+static int __devinit
+tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	return tgafb_register(&pdev->dev);
+}
+
+static void __devexit
+tgafb_pci_unregister(struct pci_dev *pdev)
+{
+	tgafb_unregister(&pdev->dev);
+}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_TC
+/*
+ *  TC registration operations
+ */
+static int __devinit tgafb_tc_register(struct device *);
+static int __devexit tgafb_tc_unregister(struct device *);
+
+static struct tc_device_id const tgafb_tc_table[] = {
+	{ "DEC     ", "PMAGD-AA" },
+	{ "DEC     ", "PMAGD   " },
+	{ }
+};
+MODULE_DEVICE_TABLE(tc, tgafb_tc_table);
+
+static struct tc_driver tgafb_tc_driver = {
+	.id_table		= tgafb_tc_table,
+	.driver			= {
+		.name		= "tgafb",
+		.bus		= &tc_bus_type,
+		.probe		= tgafb_tc_register,
+		.remove		= __devexit_p(tgafb_tc_unregister),
+	},
+};
+
+static int __devinit
+tgafb_tc_register(struct device *dev)
+{
+	int status = tgafb_register(dev);
+	if (!status)
+		get_device(dev);
+	return status;
+}
+
+static int __devexit
+tgafb_tc_unregister(struct device *dev)
+{
+	put_device(dev);
+	tgafb_unregister(dev);
+	return 0;
+}
+#endif /* CONFIG_TC */
+
 
 /**
  *      tgafb_check_var - Optional function.  Validates a var passed in.
@@ -132,10 +213,10 @@ static int
 tgafb_set_par(struct fb_info *info)
 {
 	static unsigned int const deep_presets[4] = {
-		0x00014000,
-		0x0001440d,
+		0x00004000,
+		0x0000440d,
 		0xffffffff,
-		0x0001441d
+		0x0000441d
 	};
 	static unsigned int const rasterop_presets[4] = {
 		0x00000003,
@@ -157,6 +238,8 @@ tgafb_set_par(struct fb_info *info)
 	};
 
 	struct tga_par *par = (struct tga_par *) info->par;
+	int tga_bus_pci = TGA_BUS_PCI(par->dev);
+	int tga_bus_tc = TGA_BUS_TC(par->dev);
 	u32 htimings, vtimings, pll_freq;
 	u8 tga_type;
 	int i;
@@ -221,7 +304,7 @@ tgafb_set_par(struct fb_info *info)
 	TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
 
 	/* Initalise RAMDAC. */
-	if (tga_type == TGA_TYPE_8PLANE) {
+	if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
 
 		/* Init BT485 RAMDAC registers.  */
 		BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
@@ -261,6 +344,38 @@ tgafb_set_par(struct fb_info *info)
 				      TGA_RAMDAC_REG);
 		}
 
+	} else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
+
+		/* Init BT459 RAMDAC registers.  */
+		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
+		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
+		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
+			    (par->sync_on_green ? 0xc0 : 0x40));
+
+		BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
+
+		/* Fill the palette.  */
+		BT459_LOAD_ADDR(par, 0x0000);
+		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
+
+#ifdef CONFIG_HW_CONSOLE
+		for (i = 0; i < 16; i++) {
+			int j = color_table[i];
+
+			TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
+			TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
+			TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
+		}
+		for (i = 0; i < 240 * 3; i += 4) {
+#else
+		for (i = 0; i < 256 * 3; i += 4) {
+#endif
+			TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
+			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
+			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
+			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
+		}
+
 	} else { /* 24-plane or 24plusZ */
 
 		/* Init BT463 RAMDAC registers.  */
@@ -431,6 +546,8 @@ tgafb_setcolreg(unsigned regno, unsigned
 		unsigned transp, struct fb_info *info)
 {
 	struct tga_par *par = (struct tga_par *) info->par;
+	int tga_bus_pci = TGA_BUS_PCI(par->dev);
+	int tga_bus_tc = TGA_BUS_TC(par->dev);
 
 	if (regno > 255)
 		return 1;
@@ -438,12 +555,18 @@ tgafb_setcolreg(unsigned regno, unsigned
 	green >>= 8;
 	blue >>= 8;
 
-	if (par->tga_type == TGA_TYPE_8PLANE) {
+	if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
 		BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
 		TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
 		TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
 		TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
 		TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+	} else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
+		BT459_LOAD_ADDR(par, regno);
+		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
+		TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
+		TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
+		TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
 	} else {
 		if (regno < 16) {
 			u32 value = (regno << 16) | (regno << 8) | regno;
@@ -1309,18 +1432,29 @@ static void
 tgafb_init_fix(struct fb_info *info)
 {
 	struct tga_par *par = (struct tga_par *)info->par;
+	int tga_bus_pci = TGA_BUS_PCI(par->dev);
+	int tga_bus_tc = TGA_BUS_TC(par->dev);
 	u8 tga_type = par->tga_type;
-	const char *tga_type_name;
+	const char *tga_type_name = NULL;
 
 	switch (tga_type) {
 	case TGA_TYPE_8PLANE:
-		tga_type_name = "Digital ZLXp-E1";
+		if (tga_bus_pci)
+			tga_type_name = "Digital ZLXp-E1";
+		if (tga_bus_tc)
+			tga_type_name = "Digital ZLX-E1";
 		break;
 	case TGA_TYPE_24PLANE:
-		tga_type_name = "Digital ZLXp-E2";
+		if (tga_bus_pci)
+			tga_type_name = "Digital ZLXp-E2";
+		if (tga_bus_tc)
+			tga_type_name = "Digital ZLX-E2";
 		break;
 	case TGA_TYPE_24PLUSZ:
-		tga_type_name = "Digital ZLXp-E3";
+		if (tga_bus_pci)
+			tga_type_name = "Digital ZLXp-E3";
+		if (tga_bus_tc)
+			tga_type_name = "Digital ZLX-E3";
 		break;
 	default:
 		tga_type_name = "Unknown";
@@ -1348,9 +1482,15 @@ tgafb_init_fix(struct fb_info *info)
 	info->fix.accel = FB_ACCEL_DEC_TGA;
 }
 
-static __devinit int
-tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit
+tgafb_register(struct device *dev)
 {
+	static const struct fb_videomode modedb_tc = {
+		/* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
+		"1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
+		FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
+	};
+
 	static unsigned int const fb_offset_presets[4] = {
 		TGA_8PLANE_FB_OFFSET,
 		TGA_24PLANE_FB_OFFSET,
@@ -1358,40 +1498,51 @@ tgafb_pci_register(struct pci_dev *pdev,
 		TGA_24PLUSZ_FB_OFFSET
 	};
 
+	const struct fb_videomode *modedb_tga = NULL;
+	resource_size_t bar0_start = 0, bar0_len = 0;
+	const char *mode_option_tga = NULL;
+	int tga_bus_pci = TGA_BUS_PCI(dev);
+	int tga_bus_tc = TGA_BUS_TC(dev);
+	unsigned int modedbsize_tga = 0;
 	void __iomem *mem_base;
-	unsigned long bar0_start, bar0_len;
 	struct fb_info *info;
 	struct tga_par *par;
 	u8 tga_type;
-	int ret;
+	int ret = 0;
 
 	/* Enable device in PCI config.  */
-	if (pci_enable_device(pdev)) {
+	if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
 		printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
 		return -ENODEV;
 	}
 
 	/* Allocate the fb and par structures.  */
-	info = framebuffer_alloc(sizeof(struct tga_par), &pdev->dev);
+	info = framebuffer_alloc(sizeof(struct tga_par), dev);
 	if (!info) {
 		printk(KERN_ERR "tgafb: Cannot allocate memory\n");
 		return -ENOMEM;
 	}
 
 	par = info->par;
-	pci_set_drvdata(pdev, info);
+	dev_set_drvdata(dev, info);
 
 	/* Request the mem regions.  */
-	bar0_start = pci_resource_start(pdev, 0);
-	bar0_len = pci_resource_len(pdev, 0);
 	ret = -ENODEV;
+	if (tga_bus_pci) {
+		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
+		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
+	}
+	if (tga_bus_tc) {
+		bar0_start = to_tc_dev(dev)->resource.start;
+		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
+	}
 	if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
 		printk(KERN_ERR "tgafb: cannot reserve FB region\n");
 		goto err0;
 	}
 
 	/* Map the framebuffer.  */
-	mem_base = ioremap(bar0_start, bar0_len);
+	mem_base = ioremap_nocache(bar0_start, bar0_len);
 	if (!mem_base) {
 		printk(KERN_ERR "tgafb: Cannot map MMIO\n");
 		goto err1;
@@ -1399,12 +1550,16 @@ tgafb_pci_register(struct pci_dev *pdev,
 
 	/* Grab info about the card.  */
 	tga_type = (readl(mem_base) >> 12) & 0x0f;
-	par->pdev = pdev;
+	par->dev = dev;
 	par->tga_mem_base = mem_base;
 	par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
 	par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
 	par->tga_type = tga_type;
-	pci_read_config_byte(pdev, PCI_REVISION_ID, &par->tga_chip_rev);
+	if (tga_bus_pci)
+		pci_read_config_byte(to_pci_dev(dev), PCI_REVISION_ID,
+				     &par->tga_chip_rev);
+	if (tga_bus_tc)
+		par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
 
 	/* Setup framebuffer.  */
 	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
@@ -1414,8 +1569,17 @@ tgafb_pci_register(struct pci_dev *pdev,
 	info->pseudo_palette = (void *)(par + 1);
 
 	/* This should give a reasonable default video mode.  */
-
-	ret = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL,
+	if (tga_bus_pci) {
+		mode_option_tga = mode_option_pci;
+	}
+	if (tga_bus_tc) {
+		mode_option_tga = mode_option_tc;
+		modedb_tga = &modedb_tc;
+		modedbsize_tga = 1;
+	}
+	ret = fb_find_mode(&info->var, info,
+			   mode_option ? mode_option : mode_option_tga,
+			   modedb_tga, modedbsize_tga, NULL,
 			   tga_type == TGA_TYPE_8PLANE ? 8 : 32);
 	if (ret == 0 || ret == 4) {
 		printk(KERN_ERR "tgafb: Could not find valid video mode\n");
@@ -1438,13 +1602,19 @@ tgafb_pci_register(struct pci_dev *pdev,
 		goto err1;
 	}
 
-	printk(KERN_INFO "tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
-	       par->tga_chip_rev);
-	printk(KERN_INFO "tgafb: at PCI bus %d, device %d, function %d\n",
-	       pdev->bus->number, PCI_SLOT(pdev->devfn),
-	       PCI_FUNC(pdev->devfn));
-	printk(KERN_INFO "fb%d: %s frame buffer device at 0x%lx\n",
-	       info->node, info->fix.id, bar0_start);
+	if (tga_bus_pci) {
+		pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
+			par->tga_chip_rev);
+		pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
+			to_pci_dev(dev)->bus->number,
+			PCI_SLOT(to_pci_dev(dev)->devfn),
+			PCI_FUNC(to_pci_dev(dev)->devfn));
+	}
+	if (tga_bus_tc)
+		pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
+			par->tga_chip_rev);
+	pr_info("fb%d: %s frame buffer device at 0x%lx\n",
+		info->node, info->fix.id, (long)bar0_start);
 
 	return 0;
 
@@ -1458,25 +1628,39 @@ tgafb_pci_register(struct pci_dev *pdev,
 }
 
 static void __devexit
-tgafb_pci_unregister(struct pci_dev *pdev)
+tgafb_unregister(struct device *dev)
 {
-	struct fb_info *info = pci_get_drvdata(pdev);
-	struct tga_par *par = info->par;
+	resource_size_t bar0_start = 0, bar0_len = 0;
+	int tga_bus_pci = TGA_BUS_PCI(dev);
+	int tga_bus_tc = TGA_BUS_TC(dev);
+	struct fb_info *info = NULL;
+	struct tga_par *par;
 
+	info = dev_get_drvdata(dev);
 	if (!info)
 		return;
+
+	par = info->par;
 	unregister_framebuffer(info);
 	fb_dealloc_cmap(&info->cmap);
 	iounmap(par->tga_mem_base);
-	release_mem_region(pci_resource_start(pdev, 0),
-			   pci_resource_len(pdev, 0));
+	if (tga_bus_pci) {
+		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
+		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
+	}
+	if (tga_bus_tc) {
+		bar0_start = to_tc_dev(dev)->resource.start;
+		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
+	}
+	release_mem_region(bar0_start, bar0_len);
 	framebuffer_release(info);
 }
 
 static void __devexit
 tgafb_exit(void)
 {
-	pci_unregister_driver(&tgafb_driver);
+	tc_unregister_driver(&tgafb_tc_driver);
+	pci_unregister_driver(&tgafb_pci_driver);
 }
 
 #ifndef MODULE
@@ -1505,6 +1689,7 @@ tgafb_setup(char *arg)
 static int __devinit
 tgafb_init(void)
 {
+	int status;
 #ifndef MODULE
 	char *option = NULL;
 
@@ -1512,7 +1697,10 @@ tgafb_init(void)
 		return -ENODEV;
 	tgafb_setup(option);
 #endif
-	return pci_register_driver(&tgafb_driver);
+	status = pci_register_driver(&tgafb_pci_driver);
+	if (!status)
+		status = tc_register_driver(&tgafb_tc_driver);
+	return status;
 }
 
 /*
@@ -1522,5 +1710,5 @@ tgafb_init(void)
 module_init(tgafb_init);
 module_exit(tgafb_exit);
 
-MODULE_DESCRIPTION("framebuffer driver for TGA chipset");
+MODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
 MODULE_LICENSE("GPL");
diff -up --recursive --new-file linux-mips-2.6.21-rc1-20070222.macro/include/video/tgafb.h linux-mips-2.6.21-rc1-20070222/include/video/tgafb.h
--- linux-mips-2.6.21-rc1-20070222.macro/include/video/tgafb.h	2004-12-05 05:57:54.000000000 +0000
+++ linux-mips-2.6.21-rc1-20070222/include/video/tgafb.h	2007-03-01 00:32:46.000000000 +0000
@@ -39,6 +39,7 @@
 #define	TGA_RASTEROP_REG		0x0034
 #define	TGA_PIXELSHIFT_REG		0x0038
 #define	TGA_DEEP_REG			0x0050
+#define	TGA_START_REG			0x0054
 #define	TGA_PIXELMASK_REG		0x005c
 #define	TGA_CURSOR_BASE_REG		0x0060
 #define	TGA_HORIZ_REG			0x0064
@@ -140,7 +141,7 @@
 
 
 /*
- * Useful defines for managing the BT463 on the 24-plane TGAs
+ * Useful defines for managing the BT463 on the 24-plane TGAs/SFB+s
  */
 
 #define	BT463_ADDR_LO		0x0
@@ -168,12 +169,35 @@
 #define	BT463_WINDOW_TYPE_BASE	0x0300
 
 /*
+ * Useful defines for managing the BT459 on the 8-plane SFB+s
+ */
+
+#define	BT459_ADDR_LO		0x0
+#define	BT459_ADDR_HI		0x1
+#define	BT459_REG_ACC		0x2
+#define	BT459_PALETTE		0x3
+
+#define	BT459_CUR_CLR_1		0x0181
+#define	BT459_CUR_CLR_2		0x0182
+#define	BT459_CUR_CLR_3		0x0183
+
+#define	BT459_CMD_REG_0		0x0201
+#define	BT459_CMD_REG_1		0x0202
+#define	BT459_CMD_REG_2		0x0203
+
+#define	BT459_READ_MASK		0x0204
+
+#define	BT459_BLINK_MASK	0x0206
+
+#define	BT459_CUR_CMD_REG	0x0300
+
+/*
  * The framebuffer driver private data.
  */
 
 struct tga_par {
-	/* PCI device.  */
-	struct pci_dev *pdev;
+	/* PCI/TC device.  */
+	struct device *dev;
 
 	/* Device dependent information.  */
 	void __iomem *tga_mem_base;
@@ -235,4 +259,21 @@ BT463_WRITE(struct tga_par *par, u32 m, 
 	TGA_WRITE_REG(par, m << 10 | v, TGA_RAMDAC_REG);
 }
 
+static inline void
+BT459_LOAD_ADDR(struct tga_par *par, u16 a)
+{
+	TGA_WRITE_REG(par, BT459_ADDR_LO << 2, TGA_RAMDAC_SETUP_REG);
+	TGA_WRITE_REG(par, a & 0xff, TGA_RAMDAC_REG);
+	TGA_WRITE_REG(par, BT459_ADDR_HI << 2, TGA_RAMDAC_SETUP_REG);
+	TGA_WRITE_REG(par, a >> 8, TGA_RAMDAC_REG);
+}
+
+static inline void
+BT459_WRITE(struct tga_par *par, u32 m, u16 a, u8 v)
+{
+	BT459_LOAD_ADDR(par, a);
+	TGA_WRITE_REG(par, m << 2, TGA_RAMDAC_SETUP_REG);
+	TGA_WRITE_REG(par, v, TGA_RAMDAC_REG);
+}
+
 #endif /* TGAFB_H */

From macro@linux-mips.org Thu Mar  1 13:57:51 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 13:57:55 +0000 (GMT)
Received: from pollux.ds.pg.gda.pl ([153.19.208.7]:62738 "EHLO
	pollux.ds.pg.gda.pl") by ftp.linux-mips.org with ESMTP
	id S20039435AbXCAN5r (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 13:57:47 +0000
Received: from localhost (localhost [127.0.0.1])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id E8727E1CD8;
	Thu,  1 Mar 2007 14:57:05 +0100 (CET)
X-Virus-Scanned: by amavisd-new at pollux.ds.pg.gda.pl
Received: from pollux.ds.pg.gda.pl ([127.0.0.1])
	by localhost (pollux.ds.pg.gda.pl [127.0.0.1]) (amavisd-new, port 10024)
	with ESMTP id W-xayQFJScnP; Thu,  1 Mar 2007 14:57:05 +0100 (CET)
Received: from piorun.ds.pg.gda.pl (piorun.ds.pg.gda.pl [153.19.208.8])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id 5EE5AE1CDE;
	Thu,  1 Mar 2007 14:57:05 +0100 (CET)
Received: from blysk.ds.pg.gda.pl (macro@blysk.ds.pg.gda.pl [153.19.208.6])
	by piorun.ds.pg.gda.pl (8.13.8/8.13.8) with ESMTP id l21DvFZE011392;
	Thu, 1 Mar 2007 14:57:15 +0100
Date:	Thu, 1 Mar 2007 13:57:11 +0000 (GMT)
From:	"Maciej W. Rozycki" <macro@linux-mips.org>
To:	Andrew Morton <akpm@osdl.org>
cc:	linux-kernel@vger.kernel.org, linux-mips@linux-mips.org
Subject: [PATCH 2.6.21-rc2] dz: Remove struct pt_regs references
Message-ID: <Pine.LNX.4.64N.0703011326490.25556@blysk.ds.pg.gda.pl>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
X-Virus-Scanned: ClamAV 0.90/2689/Thu Mar  1 06:46:09 2007 on piorun.ds.pg.gda.pl
X-Virus-Status:	Clean
Return-Path: <macro@linux-mips.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14298
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: macro@linux-mips.org
Precedence: bulk
X-list: linux-mips

 Remove remaining references to saved registers now that 
uart_handle_sysrq_char() does not want them.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
---

 The driver does not build without this update.

 Please apply.

  Maciej

patch-mips-2.6.21-rc1-20070222-dz-pt_regs-0
diff -up --recursive --new-file linux-mips-2.6.21-rc1-20070222.macro/drivers/serial/dz.c linux-mips-2.6.21-rc1-20070222/drivers/serial/dz.c
--- linux-mips-2.6.21-rc1-20070222.macro/drivers/serial/dz.c	2007-02-05 16:38:50.000000000 +0000
+++ linux-mips-2.6.21-rc1-20070222/drivers/serial/dz.c	2007-03-01 01:36:56.000000000 +0000
@@ -170,8 +170,7 @@ static void dz_enable_ms(struct uart_por
  * This routine deals with inputs from any lines.
  * ------------------------------------------------------------
  */
-static inline void dz_receive_chars(struct dz_port *dport_in,
-				    struct pt_regs *regs)
+static inline void dz_receive_chars(struct dz_port *dport_in)
 {
 	struct dz_port *dport;
 	struct tty_struct *tty = NULL;
@@ -226,7 +225,7 @@ static inline void dz_receive_chars(stru
 			break;
 		}
 
-		if (uart_handle_sysrq_char(&dport->port, ch, regs))
+		if (uart_handle_sysrq_char(&dport->port, ch))
 			continue;
 
 		if ((status & dport->port.ignore_status_mask) == 0) {
@@ -332,7 +331,7 @@ static irqreturn_t dz_interrupt(int irq,
 	status = dz_in(dport, DZ_CSR);
 
 	if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE))
-		dz_receive_chars(dport, regs);
+		dz_receive_chars(dport);
 
 	if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE))
 		dz_transmit_chars(dport);
patch-mips-2.6.21-rc1-20070222-dz-pt_regs-0

From ralf@linux-mips.org Thu Mar  1 18:40:46 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 18:40:48 +0000 (GMT)
Received: from localhost.localdomain ([127.0.0.1]:40932 "EHLO
	dl5rb.ham-radio-op.net") by ftp.linux-mips.org with ESMTP
	id S20039464AbXCASkq (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 18:40:46 +0000
Received: from denk.linux-mips.net (denk.linux-mips.net [127.0.0.1])
	by dl5rb.ham-radio-op.net (8.13.8/8.13.8) with ESMTP id l21IeiU6023583;
	Thu, 1 Mar 2007 18:40:44 GMT
Received: (from ralf@localhost)
	by denk.linux-mips.net (8.13.8/8.13.8/Submit) id l21Iehbx023582;
	Thu, 1 Mar 2007 18:40:43 GMT
Date:	Thu, 1 Mar 2007 18:40:43 +0000
From:	Ralf Baechle <ralf@linux-mips.org>
To:	Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
Cc:	linux-mips <linux-mips@linux-mips.org>
Subject: Re: [PATCH][MIPS] update Cobalt reserved resource
Message-ID: <20070301184043.GA23522@linux-mips.org>
References: <20070301225025.7612e583.yoichi_yuasa@tripeaks.co.jp>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <20070301225025.7612e583.yoichi_yuasa@tripeaks.co.jp>
User-Agent: Mutt/1.4.2.2i
Return-Path: <ralf@linux-mips.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14299
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: ralf@linux-mips.org
Precedence: bulk
X-list: linux-mips

On Thu, Mar 01, 2007 at 10:50:25PM +0900, Yoichi Yuasa wrote:

> This patch has removed unused timer resource.
> Moreover, the name of reserved resources ware changed.

Thanks, applied.

  Ralf

From macro@linux-mips.org Thu Mar  1 18:56:08 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 18:56:13 +0000 (GMT)
Received: from pollux.ds.pg.gda.pl ([153.19.208.7]:40709 "EHLO
	pollux.ds.pg.gda.pl") by ftp.linux-mips.org with ESMTP
	id S20039439AbXCAS4I (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 18:56:08 +0000
Received: from localhost (localhost [127.0.0.1])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id 5C146E1CCD;
	Thu,  1 Mar 2007 19:55:23 +0100 (CET)
X-Virus-Scanned: by amavisd-new at pollux.ds.pg.gda.pl
Received: from pollux.ds.pg.gda.pl ([127.0.0.1])
	by localhost (pollux.ds.pg.gda.pl [127.0.0.1]) (amavisd-new, port 10024)
	with ESMTP id QkHssIO0Ez2m; Thu,  1 Mar 2007 19:55:23 +0100 (CET)
Received: from piorun.ds.pg.gda.pl (piorun.ds.pg.gda.pl [153.19.208.8])
	by pollux.ds.pg.gda.pl (Postfix) with ESMTP id E8D1BE1C69;
	Thu,  1 Mar 2007 19:55:22 +0100 (CET)
Received: from blysk.ds.pg.gda.pl (macro@blysk.ds.pg.gda.pl [153.19.208.6])
	by piorun.ds.pg.gda.pl (8.13.8/8.13.8) with ESMTP id l21Itbkh008692;
	Thu, 1 Mar 2007 19:55:37 +0100
Date:	Thu, 1 Mar 2007 18:55:30 +0000 (GMT)
From:	"Maciej W. Rozycki" <macro@linux-mips.org>
To:	Ralf Baechle <ralf@linux-mips.org>
cc:	linux-mips@linux-mips.org
Subject: Re: [MIPS] Untangle the rest of the prom_printf mess and convert to
 early printk
In-Reply-To: <S20039493AbXCASgj/20070301183639Z+38846@ftp.linux-mips.org>
Message-ID: <Pine.LNX.4.64N.0703011853230.25556@blysk.ds.pg.gda.pl>
References: <S20039493AbXCASgj/20070301183639Z+38846@ftp.linux-mips.org>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
X-Virus-Scanned: ClamAV 0.90/2690/Thu Mar  1 12:11:27 2007 on piorun.ds.pg.gda.pl
X-Virus-Status:	Clean
Return-Path: <macro@linux-mips.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14300
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: macro@linux-mips.org
Precedence: bulk
X-list: linux-mips

On Thu, 1 Mar 2007, linux-mips@linux-mips.org wrote:

>  arch/mips/dec/prom/console.c             |   38 +--------

 Any particular reason for replacing an optimised version with this 
miserable contraption?

  Maciej

From ralf@linux-mips.org Thu Mar  1 19:10:21 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 19:10:22 +0000 (GMT)
Received: from localhost.localdomain ([127.0.0.1]:57247 "EHLO
	dl5rb.ham-radio-op.net") by ftp.linux-mips.org with ESMTP
	id S20039452AbXCATKV (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 19:10:21 +0000
Received: from denk.linux-mips.net (denk.linux-mips.net [127.0.0.1])
	by dl5rb.ham-radio-op.net (8.13.8/8.13.8) with ESMTP id l21JAK1F024287;
	Thu, 1 Mar 2007 19:10:20 GMT
Received: (from ralf@localhost)
	by denk.linux-mips.net (8.13.8/8.13.8/Submit) id l21JAJKL024286;
	Thu, 1 Mar 2007 19:10:19 GMT
Date:	Thu, 1 Mar 2007 19:10:19 +0000
From:	Ralf Baechle <ralf@linux-mips.org>
To:	"Maciej W. Rozycki" <macro@linux-mips.org>
Cc:	linux-mips@linux-mips.org
Subject: Re: [MIPS] Untangle the rest of the prom_printf mess and convert to early printk
Message-ID: <20070301191019.GA23843@linux-mips.org>
References: <S20039493AbXCASgj/20070301183639Z+38846@ftp.linux-mips.org> <Pine.LNX.4.64N.0703011853230.25556@blysk.ds.pg.gda.pl>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <Pine.LNX.4.64N.0703011853230.25556@blysk.ds.pg.gda.pl>
User-Agent: Mutt/1.4.2.2i
Return-Path: <ralf@linux-mips.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14301
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: ralf@linux-mips.org
Precedence: bulk
X-list: linux-mips

On Thu, Mar 01, 2007 at 06:55:30PM +0000, Maciej W. Rozycki wrote:

> >  arch/mips/dec/prom/console.c             |   38 +--------
> 
>  Any particular reason for replacing an optimised version with this 
> miserable contraption?

I doubt anybody will notice when the first few lines of bootup messages
take a few cycles extra.  For the moment it did matter to get rid of
the impressive barbed wire fence made from several independant early
printk implementations and macros, functions and function pointers being
named prom_printf with no apparent pattern.

If you think the code really needs to be optimized as the next step, I
take patches.

  Ralf

From stjeanma@pmc-sierra.com Thu Mar  1 20:42:59 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 20:43:05 +0000 (GMT)
Received: from father.pmc-sierra.com ([216.241.224.13]:9387 "HELO
	father.pmc-sierra.bc.ca") by ftp.linux-mips.org with SMTP
	id S20039275AbXCAUm7 (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 20:42:59 +0000
Received: (qmail 4577 invoked by uid 101); 1 Mar 2007 20:41:19 -0000
Received: from unknown (HELO pmxedge1.pmc-sierra.bc.ca) (216.241.226.183)
  by father.pmc-sierra.com with SMTP; 1 Mar 2007 20:41:19 -0000
Received: from pasqua.pmc-sierra.bc.ca (pasqua.pmc-sierra.bc.ca [134.87.183.161])
	by pmxedge1.pmc-sierra.bc.ca (8.13.4/8.12.7) with ESMTP id l21KfI9b003088
	for <linux-mips@linux-mips.org>; Thu, 1 Mar 2007 12:41:18 -0800
From:	Marc St-Jean <stjeanma@pmc-sierra.com>
Received: (from stjeanma@localhost)
	by pasqua.pmc-sierra.bc.ca (8.13.4/8.12.11) id l21KfHeh014921
	for linux-mips@linux-mips.org; Thu, 1 Mar 2007 14:41:17 -0600
Date:	Thu, 1 Mar 2007 14:41:17 -0600
Message-Id: <200703012041.l21KfHeh014921@pasqua.pmc-sierra.bc.ca>
To:	linux-mips@linux-mips.org
Subject: [PATCH 2/5] mips: PMC MSP71xx mips common
Return-Path: <stjeanma@pmc-sierra.com>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14302
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: stjeanma@pmc-sierra.com
Precedence: bulk
X-list: linux-mips

[PATCH 2/5] mips: PMC MSP71xx mips common

Patch to add mips common support for the PMC-Sierra
MSP71xx devices.

These 5 patches along with the previously posted serial patch
will boot the PMC-Sierra MSP7120 Residential Gateway board.

Thanks,
Marc

Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
---
Re-posting patch with recommended change:
Added CONFIG_NO_EXCEPT_FILL option and selected in PMC_MSP
to eliminate the .fill for exception handlers.
(arch/mips/Kconfig, arch/mips/kernel/head.S)

 arch/mips/Kconfig           |   87 ++++++++++++++++++++++
 arch/mips/Makefile          |   11 ++
 arch/mips/kernel/head.S     |    5 +
 arch/mips/kernel/traps.c    |    6 +
 include/asm-mips/bootinfo.h |   12 +++
 include/asm-mips/mipsregs.h |   30 +++++++
 include/asm-mips/regops.h   |  168 ++++++++++++++++++++++++++++++++++++++++++++
 include/asm-mips/war.h      |   11 ++
 8 files changed, 328 insertions(+), 2 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 5da6b0d..899c528 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -354,6 +354,7 @@ config MIPS_SIM
 	bool 'MIPS simulator (MIPSsim)'
 	select DMA_NONCOHERENT
 	select IRQ_CPU
+	select BOOT_RAW
 	select SYS_HAS_CPU_MIPS32_R1
 	select SYS_HAS_CPU_MIPS32_R2
 	select SYS_SUPPORTS_32BIT_KERNEL
@@ -504,6 +505,27 @@ config MACH_VR41XX
 	select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL
 	select GENERIC_HARDIRQS_NO__DO_IRQ
 
+config PMC_MSP
+	bool "PMC-Sierra MSP chipsets"
+	depends on EXPERIMENTAL
+	select DMA_NONCOHERENT
+	select SWAP_IO_SPACE
+	select NO_EXCEPT_FILL
+	select BOOT_RAW
+	select SYS_HAS_CPU_MIPS32_R1
+	select SYS_HAS_CPU_MIPS32_R2
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_BIG_ENDIAN
+	select SYS_SUPPORTS_KGDB
+	select IRQ_CPU
+	select SERIAL_8250
+	select SERIAL_8250_CONSOLE
+	help
+	  This adds support for the PMC-Sierra family of Multi-Service
+	  Processor System-On-A-Chips.  These parts include a number
+	  of integrated peripherals, interfaces and DSPs in addition to
+	  a variety of MIPS cores.
+
 config PMC_YOSEMITE
 	bool "PMC-Sierra Yosemite eval board"
 	select DMA_COHERENT
@@ -815,6 +837,55 @@ config TOSHIBA_RBTX4938
 
 endchoice
 
+choice
+	prompt "PMC-Sierra MSP SOC type"
+	depends on PMC_MSP
+
+config PMC_MSP4200_EVAL
+	bool "PMC-Sierra MSP4200 Eval Board"
+	select IRQ_MSP_SLP
+	select HW_HAS_PCI
+
+config PMC_MSP4200_GW
+	bool "PMC-Sierra MSP4200 VoIP Gateway"
+	select IRQ_MSP_SLP
+	select HW_HAS_PCI
+
+config PMC_MSP7120_EVAL
+	bool "PMC-Sierra MSP7120 Eval Board"
+	select SYS_SUPPORTS_MULTITHREADING
+	select IRQ_MSP_CIC
+	select HW_HAS_PCI
+	select MSP_USB
+
+config PMC_MSP7120_GW
+	bool "PMC-Sierra MSP7120 Residential Gateway"
+	select SYS_SUPPORTS_MULTITHREADING
+	select IRQ_MSP_CIC
+	select HW_HAS_PCI
+	select MSP_USB
+
+config PMC_MSP7120_FPGA
+	bool "PMC-Sierra MSP7120 FPGA"
+	select SYS_SUPPORTS_MULTITHREADING
+	select IRQ_MSP_CIC
+	select HW_HAS_PCI
+	select MSP_USB
+
+endchoice
+
+menu "Options for PMC-Sierra MSP chipsets"
+	depends on PMC_MSP
+
+config PMC_MSP_EMBEDDED_ROOTFS
+	bool "Root filesystem embedded in kernel image"
+	select MTD
+	select MTD_BLOCK
+	select MTD_PMC_MSP_RAMROOT
+	select MTD_RAM
+
+endmenu
+
 source "arch/mips/ddb5xxx/Kconfig"
 source "arch/mips/gt64120/ev64120/Kconfig"
 source "arch/mips/jazz/Kconfig"
@@ -879,6 +950,9 @@ config ARC
 config ARCH_MAY_HAVE_PC_FDC
 	bool
 
+config BOOT_RAW
+	bool
+
 config DMA_COHERENT
 	bool
 
@@ -924,6 +998,9 @@ config MIPS_DISABLE_OBSOLETE_IDE
 
 config GENERIC_ISA_DMA_SUPPORT_BROKEN
 	bool
+	
+config NO_EXCEPT_FILL
+	bool
 
 #
 # Endianess selection.  Sufficiently obscure so many users don't know what to
@@ -971,6 +1048,15 @@ config IRQ_CPU_RM9K
 config IRQ_MV64340
 	bool
 
+config IRQ_MSP_SLP
+	bool
+
+config IRQ_MSP_CIC
+	bool
+
+config MSP_USB
+	bool
+
 config DDB5XXX_COMMON
 	bool
 	select SYS_SUPPORTS_KGDB
@@ -1086,6 +1172,7 @@ config MIPS_L1_CACHE_SHIFT
 	int
 	default "4" if MACH_DECSTATION || SNI_RM
 	default "7" if SGI_IP27
+	default "4" if PMC_MSP4200_EVAL
 	default "5"
 
 config HAVE_STD_PC_SERIAL_PORT
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 92bca6a..1c38332 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -360,6 +360,14 @@ core-$(CONFIG_MOMENCO_OCELOT_C)	+= arch/mips/momentum/ocelot_c/
 load-$(CONFIG_MOMENCO_OCELOT_C)	+= 0xffffffff80100000
 
 #
+# PMC-Sierra MSP SOCs
+#
+core-$(CONFIG_PMC_MSP)		+= arch/mips/pmc-sierra/msp71xx/
+cflags-$(CONFIG_PMC_MSP)	+= -Iinclude/asm-mips/pmc-sierra/msp71xx \
+					-mno-branch-likely
+load-$(CONFIG_PMC_MSP)		+= 0xffffffff80100000
+
+#
 # PMC-Sierra Yosemite
 #
 core-$(CONFIG_PMC_YOSEMITE)	+= arch/mips/pmc-sierra/yosemite/
@@ -619,7 +627,8 @@ JIFFIES			= jiffies_64
 endif
 
 AFLAGS		+= $(cflags-y)
-CFLAGS		+= $(cflags-y)
+CFLAGS		+= $(cflags-y) \
+			-D"VMLINUX_LOAD_ADDRESS=$(load-y)"
 
 LDFLAGS			+= -m $(ld-emul)
 
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 6f57ca4..27366f6 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/threads.h>
 
+#include <asm/addrspace.h>
 #include <asm/asm.h>
 #include <asm/asmmacro.h>
 #include <asm/irqflags.h>
@@ -129,16 +130,18 @@
 #endif
 	.endm
 
+#ifndef CONFIG_NO_EXCEPT_FILL
 	/*
 	 * Reserved space for exception handlers.
 	 * Necessary for machines which link their kernels at KSEG0.
 	 */
 	.fill	0x400
+#endif
 
 EXPORT(stext)					# used for profiling
 EXPORT(_stext)
 
-#ifdef CONFIG_MIPS_SIM
+#ifdef CONFIG_BOOT_RAW
 	/*
 	 * Give us a fighting chance of running if execution beings at the
 	 * kernel load address.  This is needed because this platform does
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 18f56a9..2c812c2 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -70,6 +70,7 @@ extern asmlinkage void handle_reserved(void);
 extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
 	struct mips_fpu_struct *ctx, int has_fpu);
 
+void (*board_watchpoint_handler)(struct pt_regs *regs);
 void (*board_be_init)(void);
 int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
 void (*board_nmi_handler_setup)(void);
@@ -860,6 +861,11 @@ asmlinkage void do_mdmx(struct pt_regs *regs)
 
 asmlinkage void do_watch(struct pt_regs *regs)
 {
+	if (board_watchpoint_handler) {
+		(*board_watchpoint_handler)(regs);
+		return;
+	}
+	
 	/*
 	 * We use the watch exception where available to detect stack
 	 * overflows.
diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h
index c7c945b..7fc52c7 100644
--- a/include/asm-mips/bootinfo.h
+++ b/include/asm-mips/bootinfo.h
@@ -213,6 +213,18 @@
 #define MACH_GROUP_NEC_EMMA2RH 25	/* NEC EMMA2RH (was 23)		*/
 #define  MACH_NEC_MARKEINS	0	/* NEC EMMA2RH Mark-eins	*/
 
+/*
+ * Valid machtype for group PMC-MSP
+ */
+#define MACH_GROUP_MSP         26	/* PMC-Sierra MSP boards/CPUs    */
+#define MACH_MSP4200_EVAL       0	/* PMC-Sierra MSP4200 Evaluation board */
+#define MACH_MSP4200_GW         1	/* PMC-Sierra MSP4200 Gateway demo board */
+#define MACH_MSP4200_FPGA       2	/* PMC-Sierra MSP4200 Emulation board */
+#define MACH_MSP7120_EVAL       3	/* PMC-Sierra MSP7120 Evaluation board */
+#define MACH_MSP7120_GW         4	/* PMC-Sierra MSP7120 Residential Gateway board */
+#define MACH_MSP7120_FPGA       5	/* PMC-Sierra MSP7120 Emulation board */
+#define MACH_MSP_OTHER        255	/* PMC-Sierra unknown board type */
+
 #define CL_SIZE			COMMAND_LINE_SIZE
 
 const char *get_system_type(void);
diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h
index 9985cb7..bd683b6 100644
--- a/include/asm-mips/mipsregs.h
+++ b/include/asm-mips/mipsregs.h
@@ -15,6 +15,7 @@
 
 #include <linux/linkage.h>
 #include <asm/hazards.h>
+#include <asm/war.h>
 
 /*
  * The following macros are especially useful for __asm__
@@ -1292,10 +1293,39 @@ static inline void tlb_probe(void)
 
 static inline void tlb_read(void)
 {
+#if MIPS34K_MISSED_ITLB_WAR
+	int res = 0;
+
+	__asm__ __volatile__(
+	"	.set	push						\n"
+	"	.set	noreorder					\n"
+	"	.set	noat						\n"
+	"	.set	mips32r2					\n"
+	"	.word	0x41610001		# dvpe $1		\n"
+	"	move	%0, $1						\n"
+	"	ehb							\n"
+	"	.set	pop						\n"
+	: "=r" (res));
+
+	instruction_hazard();
+#endif
+
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbr\n\t"
 		".set reorder");
+
+#if MIPS34K_MISSED_ITLB_WAR
+	if ((res & _ULCAST_(1)))
+		__asm__ __volatile__(
+		"	.set	push						\n"
+		"	.set	noreorder					\n"
+		"	.set	noat						\n"
+		"	.set	mips32r2					\n"
+		"	.word	0x41600021		# evpe			\n"
+		"	ehb							\n"
+		"	.set	pop						\n");
+#endif
 }
 
 static inline void tlb_write_indexed(void)
diff --git a/include/asm-mips/war.h b/include/asm-mips/war.h
index 13a3502..74c08e6 100644
--- a/include/asm-mips/war.h
+++ b/include/asm-mips/war.h
@@ -196,6 +196,14 @@
 #endif
 
 /*
+ * 34K core erratum: "Problems Executing the TLBR Instruction"
+ */
+#if defined(CONFIG_PMC_MSP7120_EVAL) || defined(CONFIG_PMC_MSP7120_GW) || \
+	defined(CONFIG_PMC_MSP7120_FPGA)
+#define MIPS34K_MISSED_ITLB_WAR		1
+#endif
+
+/*
  * Workarounds default to off
  */
 #ifndef ICACHE_REFILLS_WORKAROUND_WAR
@@ -234,5 +242,8 @@
 #ifndef R10000_LLSC_WAR
 #define R10000_LLSC_WAR			0
 #endif
+#ifndef MIPS34K_MISSED_ITLB_WAR
+#define MIPS34K_MISSED_ITLB_WAR		0
+#endif
 
 #endif /* _ASM_WAR_H */
diff --git a/include/asm-mips/regops.h b/include/asm-mips/regops.h
new file mode 100644
index 0000000..fbfc940
--- /dev/null
+++ b/include/asm-mips/regops.h
@@ -0,0 +1,168 @@
+/*
+ * $Id: regops.h,v 1.2 2006/05/08 22:00:34 ramsayji Exp $
+ *
+ * VPE/SMP-safe functions to access registers.  They use ll/sc instructions, so
+ * it is your responsibility to ensure these are available on your platform
+ * before including this file.
+ *
+ * In addition, there is a bug on the R10000 chips which has a workaround.  If
+ * you are affected by this bug, make sure to define the symbol
+ * 'R10000_LLSC_WAR' to be non-zero.  If you are using this header from within
+ * linux, you may include <asm/war.h> before including this file to have this
+ * defined appropriately for you.
+ *
+ * Copyright 2005 PMC-Sierra, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+ *  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF USE,
+ *  DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc., 675
+ *  Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_REGOPS_H__
+#define __ASM_REGOPS_H__
+
+#ifndef R10000_LLSC_WAR
+#define R10000_LLSC_WAR 0
+#endif
+
+#if R10000_LLSC_WAR == 1
+#define __beqz	"beqzl	"
+#else
+#define __beqz	"beqz	"
+#endif
+
+#ifndef _LINUX_TYPES_H
+typedef unsigned int uint32_t;
+#endif
+
+/*
+ * Sets all the masked bits to the corresponding value bits
+ */
+static inline void set_value_reg32( volatile uint32_t * const addr,
+					uint32_t const mask,
+					uint32_t const value )
+{
+	uint32_t temp;
+
+	__asm__ __volatile__(
+	"	.set	mips3				\n"
+	"1:	ll	%0, %1	# set_value_reg32	\n"
+	"	and	%0, %2				\n"
+	"	or	%0, %3				\n"
+	"	sc	%0, %1				\n"
+	"	"__beqz"%0, 1b				\n"
+	"	.set	mips0				\n"
+	: "=&r" (temp), "=m" (*addr)
+	: "ir" (~mask), "ir" (value), "m" (*addr) );
+}
+
+/*
+ * Sets all the masked bits to '1'
+ */
+static inline void set_reg32( volatile uint32_t * const addr,
+				uint32_t const mask )
+{
+	uint32_t temp;
+
+	__asm__ __volatile__(
+	"	.set	mips3				\n"
+	"1:	ll	%0, %1		# set_reg32	\n"
+	"	or	%0, %2				\n"
+	"	sc	%0, %1				\n"
+	"	"__beqz"%0, 1b				\n"
+	"	.set	mips0				\n"
+	: "=&r" (temp), "=m" (*addr)
+	: "ir" (mask), "m" (*addr) );
+}
+
+/*
+ * Sets all the masked bits to '0'
+ */
+static inline void clear_reg32( volatile uint32_t * const addr,
+				uint32_t const mask )
+{
+	uint32_t temp;
+
+	__asm__ __volatile__(
+	"	.set	mips3				\n"
+	"1:	ll	%0, %1		# clear_reg32	\n"
+	"	and	%0, %2				\n"
+	"	sc	%0, %1				\n"
+	"	"__beqz"%0, 1b				\n"
+	"	.set	mips0				\n"
+	: "=&r" (temp), "=m" (*addr)
+	: "ir" (~mask), "m" (*addr) );
+}
+
+/*
+ * Toggles all masked bits from '0' to '1' and '1' to '0'
+ */
+static inline void toggle_reg32( volatile uint32_t * const addr,
+				uint32_t const mask )
+{
+	uint32_t temp;
+
+	__asm__ __volatile__(
+	"	.set	mips3				\n"
+	"1:	ll	%0, %1		# toggle_reg32	\n"
+	"	xor	%0, %2				\n"
+	"	sc	%0, %1				\n"
+	"	"__beqz"%0, 1b				\n"
+	"	.set	mips0				\n"
+	: "=&r" (temp), "=m" (*addr)
+	: "ir" (mask), "m" (*addr) );
+}
+
+/* For special strange cases only:
+ *
+ * If you need custom processing within a ll/sc loop, use the following macros
+ * VERY CAREFULLY:
+ *
+ *   uint32_t tmp;                      <-- Define a variable to hold the data
+ *
+ *   custom_reg32_start(address, tmp);	<-- Reads the address and puts the value
+ *						in the 'tmp' variable given
+ *
+ *	< From here on out, you are (basicly) atomic, so don't do anything too
+ *	< fancy!
+ *	< Also, this code may loop if the end of this block fails to write
+ *	< everything back safely due do the other CPU, so do NOT do anything
+ *	< with side-effects!
+ *
+ *   custom_reg32_stop(address, tmp);	<-- Writes back 'tmp' safely. 
+ *
+ */
+#define custom_reg32_read(address, tmp)				\
+	__asm__ __volatile__(					\
+	"	.set	mips3				\n"	\
+	"1:	ll	%0, %1	#custom_reg32_read	\n"	\
+	"	.set	mips0				\n"	\
+	: "=r" (tmp), "=m" (*address)				\
+	: "m" (*address) )
+
+#define custom_reg32_write(address, tmp)			\
+	__asm__ __volatile__(					\
+	"	.set	mips3				\n"	\
+	"	sc	%0, %1	#custom_reg32_write	\n"	\
+	"	"__beqz"%0, 1b				\n"	\
+	"	.set	mips0				\n"	\
+	: "=&r" (tmp), "=m" (*address)				\
+	: "0" (tmp), "m" (*address) )
+
+#endif  /* __ASM_REGOPS_H__ */

From dale@farnsworth.org Thu Mar  1 23:32:54 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 23:32:58 +0000 (GMT)
Received: from xyzzy.farnsworth.org ([65.39.95.219]:49419 "HELO farnsworth.org")
	by ftp.linux-mips.org with SMTP id S20039468AbXCAXcy (ORCPT
	<rfc822;linux-mips@linux-mips.org>); Thu, 1 Mar 2007 23:32:54 +0000
Received: (qmail 20181 invoked by uid 1000); 1 Mar 2007 16:31:48 -0700
From:	"Dale Farnsworth" <dale@farnsworth.org>
Date:	Thu, 1 Mar 2007 16:31:48 -0700
To:	Jeff Garzik <jgarzik@pobox.com>
Cc:	linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	linux-mips@linux-mips.org, Ralf Baechle <ralf@linux-mips.org>
Subject: [PATCH 1/2] mv643xx_eth: move mac_addr inside mv643xx_eth_platform_data
Message-ID: <20070301233148.GA19550@xyzzy.farnsworth.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.13 (2006-08-11)
Return-Path: <dale@farnsworth.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14303
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: dale@farnsworth.org
Precedence: bulk
X-list: linux-mips

The information contained within platform_data should be self-contained.
Replace the pointer to a MAC address with the actual MAC address in
struct mv643xx_eth_platform_data.

Signed-off-by: Dale Farnsworth <dale@farnsworth.org>

---

Replaced explicit mac address comparison with a call to is_valid_ether_addr(),
as suggested by Stephen Hemminger <shemminger@linux-foundation.org>.

 arch/mips/momentum/jaguar_atx/platform.c |   20 ++++----------------
 arch/mips/momentum/ocelot_3/platform.c   |   20 ++++----------------
 arch/mips/momentum/ocelot_c/platform.c   |   12 ++----------
 drivers/net/mv643xx_eth.c                |    2 +-
 include/linux/mv643xx.h                  |    2 +-
 5 files changed, 12 insertions(+), 44 deletions(-)

Index: b/drivers/net/mv643xx_eth.c
===================================================================
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -1380,7 +1380,7 @@ static int mv643xx_eth_probe(struct plat
 
 	pd = pdev->dev.platform_data;
 	if (pd) {
-		if (pd->mac_addr)
+		if (is_valid_ether_addr(pd->mac_addr))
 			memcpy(dev->dev_addr, pd->mac_addr, 6);
 
 		if (pd->phy_addr || pd->force_phy_addr)
Index: b/include/linux/mv643xx.h
===================================================================
--- a/include/linux/mv643xx.h
+++ b/include/linux/mv643xx.h
@@ -1289,7 +1289,6 @@ struct mv64xxx_i2c_pdata {
 #define MV643XX_ETH_NAME	"mv643xx_eth"
 
 struct mv643xx_eth_platform_data {
-	char		*mac_addr;	/* pointer to mac address */
 	u16		force_phy_addr;	/* force override if phy_addr == 0 */
 	u16		phy_addr;
 
@@ -1304,6 +1303,7 @@ struct mv643xx_eth_platform_data {
 	u32		tx_sram_size;
 	u32		rx_sram_addr;
 	u32		rx_sram_size;
+	u8		mac_addr[6];	/* mac address if non-zero*/
 };
 
 #endif /* __ASM_MV643XX_H */
Index: b/arch/mips/momentum/jaguar_atx/platform.c
===================================================================
--- a/arch/mips/momentum/jaguar_atx/platform.c
+++ b/arch/mips/momentum/jaguar_atx/platform.c
@@ -47,11 +47,7 @@ static struct resource mv64x60_eth0_reso
 	},
 };
 
-static char eth0_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth0_pd = {
-	.mac_addr	= eth0_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -80,11 +76,7 @@ static struct resource mv64x60_eth1_reso
 	},
 };
 
-static char eth1_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth1_pd = {
-	.mac_addr	= eth1_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -113,11 +105,7 @@ static struct resource mv64x60_eth2_reso
 	},
 };
 
-static char eth2_mac_addr[ETH_ALEN];
-
-static struct mv643xx_eth_platform_data eth2_pd = {
-	.mac_addr	= eth2_mac_addr,
-};
+static struct mv643xx_eth_platform_data eth2_pd;
 
 static struct platform_device eth2_device = {
 	.name		= MV643XX_ETH_NAME,
@@ -200,9 +188,9 @@ static int __init mv643xx_eth_add_pds(vo
 	int ret;
 
 	get_mac(mac);
-	eth_mac_add(eth0_mac_addr, mac, 0);
-	eth_mac_add(eth1_mac_addr, mac, 1);
-	eth_mac_add(eth2_mac_addr, mac, 2);
+	eth_mac_add(eth0_pd.mac_addr, mac, 0);
+	eth_mac_add(eth1_pd.mac_addr, mac, 1);
+	eth_mac_add(eth2_pd.mac_addr, mac, 2);
 	ret = platform_add_devices(mv643xx_eth_pd_devs,
 			ARRAY_SIZE(mv643xx_eth_pd_devs));
 
Index: b/arch/mips/momentum/ocelot_3/platform.c
===================================================================
--- a/arch/mips/momentum/ocelot_3/platform.c
+++ b/arch/mips/momentum/ocelot_3/platform.c
@@ -47,11 +47,7 @@ static struct resource mv64x60_eth0_reso
 	},
 };
 
-static char eth0_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth0_pd = {
-	.mac_addr	= eth0_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -80,11 +76,7 @@ static struct resource mv64x60_eth1_reso
 	},
 };
 
-static char eth1_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth1_pd = {
-	.mac_addr	= eth1_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -113,11 +105,7 @@ static struct resource mv64x60_eth2_reso
 	},
 };
 
-static char eth2_mac_addr[ETH_ALEN];
-
-static struct mv643xx_eth_platform_data eth2_pd = {
-	.mac_addr	= eth2_mac_addr,
-};
+static struct mv643xx_eth_platform_data eth2_pd;
 
 static struct platform_device eth2_device = {
 	.name		= MV643XX_ETH_NAME,
@@ -200,9 +188,9 @@ static int __init mv643xx_eth_add_pds(vo
 	int ret;
 
 	get_mac(mac);
-	eth_mac_add(eth0_mac_addr, mac, 0);
-	eth_mac_add(eth1_mac_addr, mac, 1);
-	eth_mac_add(eth2_mac_addr, mac, 2);
+	eth_mac_add(eth0_pd.mac_addr, mac, 0);
+	eth_mac_add(eth1_pd.mac_addr, mac, 1);
+	eth_mac_add(eth2_pd.mac_addr, mac, 2);
 	ret = platform_add_devices(mv643xx_eth_pd_devs,
 			ARRAY_SIZE(mv643xx_eth_pd_devs));
 
Index: b/arch/mips/momentum/ocelot_c/platform.c
===================================================================
--- a/arch/mips/momentum/ocelot_c/platform.c
+++ b/arch/mips/momentum/ocelot_c/platform.c
@@ -46,11 +46,7 @@ static struct resource mv64x60_eth0_reso
 	},
 };
 
-static char eth0_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth0_pd = {
-	.mac_addr	= eth0_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -79,11 +75,7 @@ static struct resource mv64x60_eth1_reso
 	},
 };
 
-static char eth1_mac_addr[ETH_ALEN];
-
 static struct mv643xx_eth_platform_data eth1_pd = {
-	.mac_addr	= eth1_mac_addr,
-
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -174,8 +166,8 @@ static int __init mv643xx_eth_add_pds(vo
 	int ret;
 
 	get_mac(mac);
-	eth_mac_add(eth0_mac_addr, mac, 0);
-	eth_mac_add(eth1_mac_addr, mac, 1);
+	eth_mac_add(eth0_pd.mac_addr, mac, 0);
+	eth_mac_add(eth1_pd.mac_addr, mac, 1);
 	ret = platform_add_devices(mv643xx_eth_pd_devs,
 			ARRAY_SIZE(mv643xx_eth_pd_devs));
 

From dale@farnsworth.org Thu Mar  1 23:33:27 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 23:33:31 +0000 (GMT)
Received: from xyzzy.farnsworth.org ([65.39.95.219]:53771 "HELO farnsworth.org")
	by ftp.linux-mips.org with SMTP id S20039480AbXCAXd1 (ORCPT
	<rfc822;linux-mips@linux-mips.org>); Thu, 1 Mar 2007 23:33:27 +0000
Received: (qmail 20364 invoked by uid 1000); 1 Mar 2007 16:33:24 -0700
From:	"Dale Farnsworth" <dale@farnsworth.org>
Date:	Thu, 1 Mar 2007 16:33:24 -0700
To:	Jeff Garzik <jgarzik@pobox.com>
Cc:	linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	linux-mips@linux-mips.org, Ralf Baechle <ralf@linux-mips.org>
Subject: [PATCH 2/2] mv643xx_eth: Place explicit port number in mv643xx_eth_platform_data
Message-ID: <20070301233324.GA20193@xyzzy.farnsworth.org>
References: <20070301233148.GA19550@xyzzy.farnsworth.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <20070301233148.GA19550@xyzzy.farnsworth.org>
User-Agent: Mutt/1.5.13 (2006-08-11)
Return-Path: <dale@farnsworth.org>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14304
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: dale@farnsworth.org
Precedence: bulk
X-list: linux-mips

We were using the platform_device.id field to identify which ethernet
port is used for mv643xx_eth device.  This is not generally correct.
It will be incorrect, for example, if a hardware platform uses a single
port but not the first port.  Here, we add an explicit port_number field
to struct mv643xx_eth_platform_data.

This makes the mv643xx_eth_platform_data structure required, but that
isn't an issue since all users currently provide it already.

Signed-off-by: Dale Farnsworth <dale@farnsworth.org>

---

 arch/mips/momentum/jaguar_atx/platform.c  |    8 ++
 arch/mips/momentum/ocelot_3/platform.c    |    8 ++
 arch/mips/momentum/ocelot_c/platform.c    |    4 +
 arch/powerpc/platforms/chrp/pegasos_eth.c |    2 
 arch/ppc/syslib/mv64x60.c                 |   12 +++-
 drivers/net/mv643xx_eth.c                 |   59 ++++++++++----------
 include/linux/mv643xx.h                   |    1 
 7 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/arch/mips/momentum/jaguar_atx/platform.c b/arch/mips/momentum/jaguar_atx/platform.c
index 035ea51..003d3ee 100644
Index: b/arch/mips/momentum/jaguar_atx/platform.c
===================================================================
--- a/arch/mips/momentum/jaguar_atx/platform.c
+++ b/arch/mips/momentum/jaguar_atx/platform.c
@@ -48,6 +48,8 @@ static struct resource mv64x60_eth0_reso
 };
 
 static struct mv643xx_eth_platform_data eth0_pd = {
+	.port_number	= 0,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -77,6 +79,8 @@ static struct resource mv64x60_eth1_reso
 };
 
 static struct mv643xx_eth_platform_data eth1_pd = {
+	.port_number	= 1,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -105,7 +109,9 @@ static struct resource mv64x60_eth2_reso
 	},
 };
 
-static struct mv643xx_eth_platform_data eth2_pd;
+static struct mv643xx_eth_platform_data eth2_pd = {
+	.port_number	= 2,
+};
 
 static struct platform_device eth2_device = {
 	.name		= MV643XX_ETH_NAME,
Index: b/arch/mips/momentum/ocelot_3/platform.c
===================================================================
--- a/arch/mips/momentum/ocelot_3/platform.c
+++ b/arch/mips/momentum/ocelot_3/platform.c
@@ -48,6 +48,8 @@ static struct resource mv64x60_eth0_reso
 };
 
 static struct mv643xx_eth_platform_data eth0_pd = {
+	.port_number	= 0,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -77,6 +79,8 @@ static struct resource mv64x60_eth1_reso
 };
 
 static struct mv643xx_eth_platform_data eth1_pd = {
+	.port_number	= 1,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -105,7 +109,9 @@ static struct resource mv64x60_eth2_reso
 	},
 };
 
-static struct mv643xx_eth_platform_data eth2_pd;
+static struct mv643xx_eth_platform_data eth2_pd = {
+	.port_number	= 2,
+};
 
 static struct platform_device eth2_device = {
 	.name		= MV643XX_ETH_NAME,
Index: b/arch/mips/momentum/ocelot_c/platform.c
===================================================================
--- a/arch/mips/momentum/ocelot_c/platform.c
+++ b/arch/mips/momentum/ocelot_c/platform.c
@@ -47,6 +47,8 @@ static struct resource mv64x60_eth0_reso
 };
 
 static struct mv643xx_eth_platform_data eth0_pd = {
+	.port_number	= 0,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH0,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
@@ -76,6 +78,8 @@ static struct resource mv64x60_eth1_reso
 };
 
 static struct mv643xx_eth_platform_data eth1_pd = {
+	.port_number	= 1,
+
 	.tx_sram_addr	= MV_SRAM_BASE_ETH1,
 	.tx_sram_size	= MV_SRAM_TXRING_SIZE,
 	.tx_queue_size	= MV_SRAM_TXRING_SIZE / 16,
Index: b/arch/powerpc/platforms/chrp/pegasos_eth.c
===================================================================
--- a/arch/powerpc/platforms/chrp/pegasos_eth.c
+++ b/arch/powerpc/platforms/chrp/pegasos_eth.c
@@ -58,6 +58,7 @@ static struct resource mv643xx_eth0_reso
 
 
 static struct mv643xx_eth_platform_data eth0_pd = {
+	.port_number	= 0,
 	.tx_sram_addr = PEGASOS2_SRAM_BASE_ETH0,
 	.tx_sram_size = PEGASOS2_SRAM_TXRING_SIZE,
 	.tx_queue_size = PEGASOS2_SRAM_TXRING_SIZE/16,
@@ -87,6 +88,7 @@ static struct resource mv643xx_eth1_reso
 };
 
 static struct mv643xx_eth_platform_data eth1_pd = {
+	.port_number	= 1,
 	.tx_sram_addr = PEGASOS2_SRAM_BASE_ETH1,
 	.tx_sram_size = PEGASOS2_SRAM_TXRING_SIZE,
 	.tx_queue_size = PEGASOS2_SRAM_TXRING_SIZE/16,
Index: b/arch/ppc/syslib/mv64x60.c
===================================================================
--- a/arch/ppc/syslib/mv64x60.c
+++ b/arch/ppc/syslib/mv64x60.c
@@ -339,7 +339,9 @@ static struct resource mv64x60_eth0_reso
 	},
 };
 
-static struct mv643xx_eth_platform_data eth0_pd;
+static struct mv643xx_eth_platform_data eth0_pd = {
+	.port_number	= 0,
+};
 
 static struct platform_device eth0_device = {
 	.name		= MV643XX_ETH_NAME,
@@ -362,7 +364,9 @@ static struct resource mv64x60_eth1_reso
 	},
 };
 
-static struct mv643xx_eth_platform_data eth1_pd;
+static struct mv643xx_eth_platform_data eth1_pd = {
+	.port_number	= 1,
+};
 
 static struct platform_device eth1_device = {
 	.name		= MV643XX_ETH_NAME,
@@ -385,7 +389,9 @@ static struct resource mv64x60_eth2_reso
 	},
 };
 
-static struct mv643xx_eth_platform_data eth2_pd;
+static struct mv643xx_eth_platform_data eth2_pd = {
+	.port_number	= 2,
+};
 
 static struct platform_device eth2_device = {
 	.name		= MV643XX_ETH_NAME,
Index: b/drivers/net/mv643xx_eth.c
===================================================================
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -1309,7 +1309,7 @@ static void mv643xx_init_ethtool_cmd(str
 static int mv643xx_eth_probe(struct platform_device *pdev)
 {
 	struct mv643xx_eth_platform_data *pd;
-	int port_num = pdev->id;
+	int port_num;
 	struct mv643xx_private *mp;
 	struct net_device *dev;
 	u8 *p;
@@ -1319,6 +1319,12 @@ static int mv643xx_eth_probe(struct plat
 	int duplex = DUPLEX_HALF;
 	int speed = 0;			/* default to auto-negotiation */
 
+	pd = pdev->dev.platform_data;
+	if (pd == NULL) {
+		printk(KERN_ERR "No mv643xx_eth_platform_data\n");
+		return -ENODEV;
+	}
+
 	dev = alloc_etherdev(sizeof(struct mv643xx_private));
 	if (!dev)
 		return -ENOMEM;
@@ -1331,8 +1337,6 @@ static int mv643xx_eth_probe(struct plat
 	BUG_ON(!res);
 	dev->irq = res->start;
 
-	mp->port_num = port_num;
-
 	dev->open = mv643xx_eth_open;
 	dev->stop = mv643xx_eth_stop;
 	dev->hard_start_xmit = mv643xx_eth_start_xmit;
@@ -1373,39 +1377,40 @@ static int mv643xx_eth_probe(struct plat
 
 	spin_lock_init(&mp->lock);
 
+	port_num = pd->port_number;
+
 	/* set default config values */
 	eth_port_uc_addr_get(dev, dev->dev_addr);
 	mp->rx_ring_size = MV643XX_ETH_PORT_DEFAULT_RECEIVE_QUEUE_SIZE;
 	mp->tx_ring_size = MV643XX_ETH_PORT_DEFAULT_TRANSMIT_QUEUE_SIZE;
 
-	pd = pdev->dev.platform_data;
-	if (pd) {
-		if (is_valid_ether_addr(pd->mac_addr))
-			memcpy(dev->dev_addr, pd->mac_addr, 6);
-
-		if (pd->phy_addr || pd->force_phy_addr)
-			ethernet_phy_set(port_num, pd->phy_addr);
-
-		if (pd->rx_queue_size)
-			mp->rx_ring_size = pd->rx_queue_size;
-
-		if (pd->tx_queue_size)
-			mp->tx_ring_size = pd->tx_queue_size;
-
-		if (pd->tx_sram_size) {
-			mp->tx_sram_size = pd->tx_sram_size;
-			mp->tx_sram_addr = pd->tx_sram_addr;
-		}
+	if (is_valid_ether_addr(pd->mac_addr))
+		memcpy(dev->dev_addr, pd->mac_addr, 6);
 
-		if (pd->rx_sram_size) {
-			mp->rx_sram_size = pd->rx_sram_size;
-			mp->rx_sram_addr = pd->rx_sram_addr;
-		}
+	if (pd->phy_addr || pd->force_phy_addr)
+		ethernet_phy_set(port_num, pd->phy_addr);
+
+	if (pd->rx_queue_size)
+		mp->rx_ring_size = pd->rx_queue_size;
+
+	if (pd->tx_queue_size)
+		mp->tx_ring_size = pd->tx_queue_size;
+
+	if (pd->tx_sram_size) {
+		mp->tx_sram_size = pd->tx_sram_size;
+		mp->tx_sram_addr = pd->tx_sram_addr;
+	}
 
-		duplex = pd->duplex;
-		speed = pd->speed;
+	if (pd->rx_sram_size) {
+		mp->rx_sram_size = pd->rx_sram_size;
+		mp->rx_sram_addr = pd->rx_sram_addr;
 	}
 
+	duplex = pd->duplex;
+	speed = pd->speed;
+
+	mp->port_num = port_num;
+
 	/* Hook up MII support for ethtool */
 	mp->mii.dev = dev;
 	mp->mii.mdio_read = mv643xx_mdio_read;
Index: b/include/linux/mv643xx.h
===================================================================
--- a/include/linux/mv643xx.h
+++ b/include/linux/mv643xx.h
@@ -1289,6 +1289,7 @@ struct mv64xxx_i2c_pdata {
 #define MV643XX_ETH_NAME	"mv643xx_eth"
 
 struct mv643xx_eth_platform_data {
+	int		port_number;
 	u16		force_phy_addr;	/* force override if phy_addr == 0 */
 	u16		phy_addr;
 

From thomas.koeller@baslerweb.com Thu Mar  1 23:54:02 2007
Received: with ECARTIS (v1.0.0; list linux-mips); Thu, 01 Mar 2007 23:54:08 +0000 (GMT)
Received: from mail04.hansenet.de ([213.191.73.12]:38353 "EHLO
	webmail.hansenet.de") by ftp.linux-mips.org with ESMTP
	id S20039480AbXCAXyC (ORCPT <rfc822;linux-mips@linux-mips.org>);
	Thu, 1 Mar 2007 23:54:02 +0000
Received: from [80.171.15.212] (80.171.15.212) by webmail.hansenet.de (7.2.074) (authenticated as mbx20228207@koeller-hh.org)
        id 45CB2EBD00923740; Fri, 2 Mar 2007 00:50:09 +0100
Received: from localhost.koeller.dyndns.org (localhost.koeller.dyndns.org [127.0.0.1])
	by mail.koeller.dyndns.org (Postfix) with ESMTP id 187ED479E4;
	Fri,  2 Mar 2007 00:50:08 +0100 (CET)
From:	Thomas Koeller <thomas.koeller@baslerweb.com>
Date:	Fri, 2 Mar 2007 00:50:07 +0100
Subject: [PATCH] excite: Add image capturing driver
X-Length: 51609
X-UID:	11
To:	ralf@linux-mips.org
Cc:	linux-mips@linux-mips.org
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Message-Id: <200703020050.07717.thomas.koeller@baslerweb.com>
Return-Path: <thomas.koeller@baslerweb.com>
X-Envelope-To: <"|/home/ecartis/ecartis -s linux-mips"> (uid 0)
X-Orcpt: rfc822;linux-mips@linux-mips.org
Original-Recipient: rfc822;linux-mips@linux-mips.org
X-archive-position: 14305
X-ecartis-version: Ecartis v1.0.0
Sender: linux-mips-bounce@linux-mips.org
Errors-to: linux-mips-bounce@linux-mips.org
X-original-sender: thomas.koeller@baslerweb.com
Precedence: bulk
X-list: linux-mips

This is the excite's image capturing driver. As it does not fit in
any of the existing driver categories and is truly platform specific
(what good is a camera that cannot capture images?), it is included
in the platform.

Signed-off-by: Thomas Koeller <thomas.koeller@baslerweb.com>
---
 arch/mips/Kconfig                    |   11 +-
 arch/mips/basler/excite/Kconfig      |   29 +
 arch/mips/basler/excite/Makefile     |    3 +
 arch/mips/basler/excite/xicap.h      |   40 ++
 arch/mips/basler/excite/xicap_core.c |  472 ++++++++++++++
 arch/mips/basler/excite/xicap_gpi.c  | 1184 
++++++++++++++++++++++++++++++++++
 arch/mips/basler/excite/xicap_priv.h |   48 ++
 7 files changed, 1777 insertions(+), 10 deletions(-)
 create mode 100644 arch/mips/basler/excite/Kconfig
 create mode 100644 arch/mips/basler/excite/xicap.h
 create mode 100644 arch/mips/basler/excite/xicap_core.c
 create mode 100644 arch/mips/basler/excite/xicap_gpi.c
 create mode 100644 arch/mips/basler/excite/xicap_priv.h

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 3315a71..498b0b8 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -145,20 +145,11 @@ config BASLER_EXCITE
 	select SYS_SUPPORTS_64BIT_KERNEL
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select SYS_SUPPORTS_KGDB
+	source "arch/mips/basler/excite/Kconfig"
 	help
 	  The eXcite is a smart camera platform manufactured by
 	  Basler Vision Technologies AG.
 
-config BASLER_EXCITE_PROTOTYPE
-	bool "Support for pre-release units"
-	depends on BASLER_EXCITE
-	default n
-	help
-	  Pre-series (prototype) units are different from later ones in
-	  some ways. Select this option if you have one of these. Please
-	  note that a kernel built with this option selected will not be
-	  able to run on normal units.
-
 config MIPS_COBALT
 	bool "Cobalt Server"
 	select DMA_NONCOHERENT
diff --git a/arch/mips/basler/excite/Kconfig b/arch/mips/basler/excite/Kconfig
new file mode 100644
index 0000000..3270815
--- /dev/null
+++ b/arch/mips/basler/excite/Kconfig
@@ -0,0 +1,29 @@
+config BASLER_EXCITE_PROTOTYPE
+	bool "Support for pre-release units"
+	depends on BASLER_EXCITE
+	default n
+	help
+	  Pre-series (prototype) units are different from later ones in
+	  some ways. Select this option if you have one of these. Please
+	  note that a kernel built with this option selected will not be
+	  able to run on normal units.
+
+config BASLER_EXCITE_XICAP
+	tristate "Image capturing driver"
+	depends on BASLER_EXCITE
+	default m
+	help
+	  Enable basic platform-specific support for frame capture devices.
+	  You do not want to disable this, because that would give you a
+	  camera incapable of capturing images, which is kind of pointless.
+	  This can also be compiled as a module, which will be named
+	  xicap_core.
+
+config BASLER_EXCITE_XICAP_GPI
+	tristate "Image capturing via GPI"
+	depends on BASLER_EXCITE_XICAP && GPI_RM9000
+	default m
+	help
+	  Image capturing driver for the GPI hardware found on RM9xxx
+	  chips manufactured by PMC-Sierra.
+
diff --git a/arch/mips/basler/excite/Makefile 
b/arch/mips/basler/excite/Makefile
index 519142c..1dd26a4 100644
--- a/arch/mips/basler/excite/Makefile
+++ b/arch/mips/basler/excite/Makefile
@@ -7,3 +7,6 @@ obj-$(CONFIG_BASLER_EXCITE)	+= excite_irq.o excite_prom.o 
excite_setup.o \
 
 obj-$(CONFIG_KGDB)		+= excite_dbg_io.o
 obj-m				+= excite_iodev.o
+
+obj-$(CONFIG_BASLER_EXCITE_XICAP)	+= xicap_core.o
+obj-$(CONFIG_BASLER_EXCITE_XICAP_GPI)	+= xicap_gpi.o
diff --git a/arch/mips/basler/excite/xicap.h b/arch/mips/basler/excite/xicap.h
new file mode 100644
index 0000000..6614bc4
--- /dev/null
+++ b/arch/mips/basler/excite/xicap.h
@@ -0,0 +1,40 @@
+#if ! defined(XICAP_H)
+#define XICAP_H
+
+#include <linux/ioctl.h>
+
+/* A buffer descriptor. */
+typedef struct {
+	void	*data;			/* data buffer */
+	size_t	size;			/* data buffer size */
+	void	*ctxt;			/* user-defined context pointer */
+} xicap_arg_qbuf_t;
+
+
+/*
+ * Result block passed back to user after operation completed.
+ * Todo: add time stamp field.
+ */
+typedef struct {
+	void		*data;		/* data buffer pointer */
+	void		*ctxt;		/* user context */
+	int		status;		/* buffer status, see below */
+} xicap_result_t;
+
+/* Returned buffer status values */
+#define XICAP_BUFSTAT_OK	0	/* normal return */
+#define XICAP_BUFSTAT_ABORTED	1	/* aborted by flush */
+#define XICAP_BUFSTAT_VMERR	2	/* buffer mapping error */
+
+
+
+/* Definitions for ioctl() */
+#define	XICAP_IOC_TYPE		0xbb	/* a random choice */
+
+/* Ready to grab next frame */
+#define XICAP_IOC_QBUF		_IOW(XICAP_IOC_TYPE, 0, xicap_arg_qbuf_t)
+#define XICAP_IOC_FLUSH		_IO(XICAP_IOC_TYPE, 1)
+
+#define XICAP_IOC_MAXNR		1
+
+#endif	/* ! defined(XICAP_H) */
diff --git a/arch/mips/basler/excite/xicap_core.c 
b/arch/mips/basler/excite/xicap_core.c
new file mode 100644
index 0000000..281a99c
--- /dev/null
+++ b/arch/mips/basler/excite/xicap_core.c
@@ -0,0 +1,472 @@
+/*
+ *  Copyright (C) 2004...2007 by Basler Vision Technologies AG
+ *  Author: Thomas Koeller <thomas.koeller@baslerweb.com>
+ *
+ *  This file contains basic support for image capturing on the
+ *  Basler eXcite intelligent camera platform, to be used by a
+ *  hardware-specific capturing driver.
+ */
+
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+
+#include "xicap.h"
+#include "xicap_priv.h"
+
+
+
+/* Get device context from inode */
+#define cxi(__i__) container_of((__i__)->i_cdev, xicap_device_context_t, 
chardev)
+
+/* Get device context from file */
+#define cxf(__f__) ((xicap_device_context_t *) (__f__)->private_data)
+
+
+
+/* String constants */
+static const char xicap_name[] = "xicap";
+
+
+
+/* Data used for device number allocation */
+static dev_t devnum_base;
+static DECLARE_MUTEX(devnum_lock);
+static unsigned long devnum_bitmap = 0;
+
+#define MAX_DEVICES	(sizeof devnum_bitmap * 8)
+
+
+
+/* Function prototypes */
+static void xicap_device_release(struct class_device *);
+static long xicap_ioctl(struct file *, unsigned int, unsigned long);
+static unsigned int xicap_poll(struct file *, poll_table *);
+static ssize_t xicap_read(struct file *, char __user *, size_t, loff_t *);
+static int xicap_open(struct inode *, struct file *);
+static int xicap_release(struct inode *, struct file *);
+static int xicap_queue_buffer(xicap_device_context_t *,
+			      const xicap_arg_qbuf_t *);
+
+
+
+/* A class for xicap devices */
+static struct class xicap_class = {
+	.name		= (char *) xicap_name,
+	.release	= xicap_device_release,
+	.class_release	= NULL
+};
+
+
+
+/* The file operations vector */
+static struct file_operations xicap_fops = {
+	.unlocked_ioctl	= xicap_ioctl,
+	.read		= xicap_read,
+	.open		= xicap_open,
+	.release	= xicap_release,
+	.poll		= xicap_poll
+};
+
+
+
+struct xicap_devctxt {
+	struct class_device		classdev;
+	struct cdev			chardev;
+	const xicap_hw_driver_t *	hwdrv;
+	dev_t				devnum;
+	spinlock_t			compl_lock;
+	struct list_head		compl_queue;
+	atomic_t			opencnt;
+	wait_queue_head_t		wq;
+};
+
+
+
+/* A context for every class device */
+struct xicap_clsdev_ctxt {
+	xicap_hw_driver_t *	hwdrv;
+};
+
+
+
+/* Check for completed buffers */
+static inline int xicap_check_completed(xicap_device_context_t *dc)
+{
+	int r;
+	spin_lock(&dc->compl_lock);
+	r = !list_empty(&dc->compl_queue);
+	spin_unlock(&dc->compl_lock);
+	return r;
+}
+
+
+
+/* Retreive a completed buffer from the queue */
+static inline xicap_data_buffer_t *
+xicap_retreive_completed(xicap_device_context_t *dc)
+{
+	xicap_data_buffer_t * p;
+	spin_lock(&dc->compl_lock);
+	p = list_empty(&dc->compl_queue) ?
+		NULL : list_entry(dc->compl_queue.next, xicap_data_buffer_t,
+				  link);
+	if (p)
+		list_del(&p->link);
+	spin_unlock(&dc->compl_lock);
+	return p;
+}
+
+
+
+
+static long xicap_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+	int res = -ENOTTY;
+	union {
+		xicap_arg_qbuf_t qbuf;
+	} a;
+
+
+	if (unlikely(_IOC_TYPE(cmd) != XICAP_IOC_TYPE))
+		return -ENOTTY;
+
+	if ((_IOC_DIR(cmd) & _IOC_READ)
+	    && !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd)))
+		return -EFAULT;
+
+	if ((_IOC_DIR(cmd) & _IOC_WRITE)
+	    && !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd)))
+		return -EFAULT;
+
+	switch (cmd) {
+		case XICAP_IOC_QBUF:
+			if (_IOC_SIZE(XICAP_IOC_QBUF) != sizeof a.qbuf) {
+				res = -EINVAL;
+				break;
+			}
+			res = __copy_from_user(&a.qbuf, (void *) arg,
+					       sizeof a.qbuf) ?
+				-EINVAL : xicap_queue_buffer(cxf(f), &a.qbuf);
+			break;
+
+		case XICAP_IOC_FLUSH:
+			{
+				xicap_device_context_t * const dc = cxf(f);
+				res = dc->hwdrv->flush ?
+				      dc->hwdrv->flush(dc->classdev.dev) :
+				      -ENOTTY;
+			}
+			break;
+	}
+
+	return res;
+}
+
+
+
+static unsigned int xicap_poll(struct file *f, poll_table *tbl)
+{
+	xicap_device_context_t * const dc = cxf(f);
+	poll_wait(f, &dc->wq, tbl);
+	return POLLOUT | POLLWRNORM
+	       | (xicap_check_completed(dc) ? POLLIN | POLLRDNORM : 0);
+}
+
+
+
+static ssize_t xicap_read(struct file *f, char __user *buf, size_t len,
+			  loff_t *offs)
+{
+	int res;
+	xicap_device_context_t * const dc = cxf(f);
+	xicap_data_buffer_t *bd;
+	xicap_result_t dat;
+
+	if (unlikely(len != sizeof dat))
+		return -EINVAL;
+
+	if (unlikely(!access_ok(VERIFY_WRITE, buf, len)))
+		return -EFAULT;
+
+	if (f->f_flags & O_NONBLOCK) {
+		/* If nonblocking and no completed ops, return error status */
+		if (bd = xicap_retreive_completed(dc), !bd)
+			return -EAGAIN;
+	} else {
+		res = wait_event_interruptible(
+			dc->wq,
+			(bd = xicap_retreive_completed(dc), bd));
+		if (res)
+			return res;
+	}
+
+	if (dc->hwdrv->finish_buffer) {
+		const int i = dc->hwdrv->finish_buffer(dc->classdev.dev,
+						       bd->frmctxt);
+		if (i) {
+			kfree(bd);
+			return i;
+		}
+	}
+
+	dat.data = bd->uaddr;
+	dat.ctxt = bd->uctxt;
+	dat.status = bd->status;
+	__copy_to_user(buf, &dat, sizeof dat);
+	kfree(bd);
+
+	return sizeof dat;
+}
+
+
+
+static int xicap_open(struct inode *i, struct file *f)
+{
+	xicap_device_context_t * const dc = cxi(i);
+
+	/* Only one opener allowed */
+	if (atomic_dec_if_positive(&dc->opencnt) < 0)
+		return -EBUSY;
+
+	spin_lock_init(&dc->compl_lock);
+	INIT_LIST_HEAD(&dc->compl_queue);
+	init_waitqueue_head(&dc->wq);
+	f->private_data = cxi(i);
+	return dc->hwdrv->start(dc->classdev.dev);
+}
+
+
+
+static int xicap_release(struct inode *i, struct file *f)
+{
+	xicap_device_context_t * const dc = cxi(i);
+	dc->hwdrv->stop(dc->classdev.dev);
+
+	while (xicap_check_completed(dc))
+		kfree(xicap_retreive_completed(dc));
+
+	atomic_set(&dc->opencnt, 1);
+	return 0;
+}
+
+
+
+/* Device registration */
+xicap_device_context_t *
+xicap_device_register(struct device *dev, const xicap_hw_driver_t *hwdrv)
+{
+	int res = 0, devi = -1;
+
+	/* Set up a device context */
+	xicap_device_context_t * const dc =
+		(xicap_device_context_t *) kmalloc(sizeof *dc, GFP_KERNEL);
+	if (!dc) {
+		res = -ENOMEM;
+		goto ex;
+	}
+
+	memset(dc, 0, sizeof *dc);
+	cdev_init(&dc->chardev, &xicap_fops);
+	dc->chardev.owner = THIS_MODULE;
+	dc->classdev.dev = get_device(dev);
+	dc->hwdrv = hwdrv;
+	atomic_set(&dc->opencnt, 1);
+
+	/* Allocate a device number */
+	down(&devnum_lock);
+	if (unlikely(devnum_bitmap == ~0x0)) {
+		up(&devnum_lock);
+		res = -ENODEV;
+		goto ex;
+	}
+	devi = ffz(devnum_bitmap);
+	devnum_bitmap |= 0x1 << devi;
+	up(&devnum_lock);
+
+	/* Register the class device with its class */
+	dc->classdev.class = &xicap_class;
+	dc->classdev.devt = devi + devnum_base;
+	res = snprintf(dc->classdev.class_id, sizeof dc->classdev.class_id,
+		       "%s%u", xicap_name, devi)
+		< sizeof dc->classdev.class_id ? 0 : -ENAMETOOLONG;
+	if (!res)
+		res = class_device_register(&dc->classdev);
+	if (unlikely(res)) {
+		dc->classdev.class = NULL;
+		goto ex;
+	}
+
+	/* Register the character device */
+	res = cdev_add(&dc->chardev, devi + devnum_base, 1);
+
+ex:
+	if (res) {
+		if (dc->classdev.class)
+			class_device_unregister(&dc->classdev);
+		if (devi >= 0) {
+			down(&devnum_lock);
+			devnum_bitmap &= ~(0x1 << devi);
+			up(&devnum_lock);
+		}
+		if (dc) {
+			put_device(dc->classdev.dev);
+			kfree(dc);
+		}
+	} else {
+		dc->devnum = devi + devnum_base;
+	}
+
+	return res ? (xicap_device_context_t *) ERR_PTR(res) : dc;
+}
+
+
+
+/* Device unregistration */
+void xicap_device_unregister(xicap_device_context_t *dc)
+{
+	cdev_del(&dc->chardev);
+	class_device_unregister(&dc->classdev);
+	down(&devnum_lock);
+	devnum_base &= ~(0x1 << (dc->devnum - devnum_base));
+	up(&devnum_lock);
+}
+
+
+
+void xicap_frame_done(xicap_device_context_t *dc, xicap_data_buffer_t *bd)
+{
+	struct page **p;
+
+	for (p = bd->pages; p < bd->pages + bd->npages; p++)
+		page_cache_release(*p);
+
+	spin_lock(&dc->compl_lock);
+	list_add_tail(&bd->link, &dc->compl_queue);
+	spin_unlock(&dc->compl_lock);
+	wake_up_interruptible(&dc->wq);
+}
+
+
+
+static void xicap_device_release(struct class_device *cldev)
+{
+	xicap_device_context_t * const dc =
+		container_of(cldev, xicap_device_context_t, classdev);
+	put_device(dc->classdev.dev);
+	kfree(dc);
+}
+
+
+
+static int xicap_queue_buffer(xicap_device_context_t *dc,
+			      const xicap_arg_qbuf_t *arg)
+{
+	int res, npages;
+	xicap_data_buffer_t *bufdesc;
+
+	/* Check for buffer write permissions */
+	if (!access_ok(VERIFY_WRITE, arg->data, arg->size))
+		return -EFAULT;
+
+	npages = (PAGE_ALIGN((unsigned long ) arg->data + arg->size)
+		 - ((unsigned long ) arg->data & PAGE_MASK)) >> PAGE_SHIFT;
+
+	bufdesc = (xicap_data_buffer_t *)
+		kmalloc(sizeof *bufdesc + sizeof (struct page *) * npages,
+			GFP_KERNEL);
+	if (!bufdesc)
+		return -ENOMEM;
+
+	bufdesc->uctxt = arg->ctxt;
+	bufdesc->uaddr = arg->data;
+	bufdesc->size = arg->size;
+	bufdesc->npages = npages;
+
+	/* Get hold of the data buffer pages */
+	res = get_user_pages(current, current->mm, (unsigned long) arg->data,
+			     npages, 1, 0, bufdesc->pages, NULL);
+	if (res < 0) {
+		kfree(bufdesc);
+		return res;
+	}
+
+	bufdesc->frmctxt = dc->hwdrv->do_buffer(dc->classdev.dev, bufdesc);
+	if (IS_ERR(bufdesc->frmctxt))  {
+		int i;
+
+		for (i = 0; i < npages; i++)
+			put_page(bufdesc->pages[i]);
+		i = PTR_ERR(bufdesc->frmctxt);
+		kfree(bufdesc);
+		return i;
+	}
+
+	return 0;
+}
+
+
+
+static int __init xicap_init_module(void)
+{
+	int res;
+	static __initdata char
+		errfmt[] = KERN_ERR "%s: %s failed - error = %d\n",
+		clsreg[] = "class registration",
+		devalc[] = "device number allocation";
+
+	/* Register the xicap class */
+	res = class_register(&xicap_class);
+	if (unlikely(res)) {
+		printk(errfmt, xicap_class.name, clsreg, res);
+		return res;
+	}
+
+	/* Allocate a range of device numbers */
+	res = alloc_chrdev_region(&devnum_base, 0, MAX_DEVICES, xicap_name);
+	if (unlikely(res)) {
+		printk(errfmt, xicap_name, devalc, res);
+		class_unregister(&xicap_class);
+		return res;
+	}
+
+	return 0;
+}
+
+
+
+static void __exit xicap_cleanup_module(void)
+{
+	unregister_chrdev_region(devnum_base, MAX_DEVICES);
+	class_unregister(&xicap_class);
+}
+
+
+
+EXPORT_SYMBOL(xicap_device_register);
+EXPORT_SYMBOL(xicap_device_unregister);
+EXPORT_SYMBOL(xicap_frame_done);
+module_init(xicap_init_module);
+module_exit(xicap_cleanup_module);
+
+
+
+MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
+MODULE_DESCRIPTION("Basler eXcite frame capturing core driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/arch/mips/basler/excite/xicap_gpi.c 
b/arch/mips/basler/excite/xicap_gpi.c
new file mode 100644
index 0000000..29ac093
--- /dev/null
+++ b/arch/mips/basler/excite/xicap_gpi.c
@@ -0,0 +1,1184 @@
+/*
+ *  Copyright (C) 2004...2007 by Basler Vision Technologies AG
+ *  Author: Thomas Koeller <thomas.koeller@baslerweb.com>
+ *
+ *  This driver uses one of the GPI channels present on the
+ *  eXcite's RM9122 SoC to implement image data capturing.
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/rm9k-ocd.h>
+#include <asm/page.h>
+#include <asm/semaphore.h>
+
+#include <rm9k_xicap.h>
+
+#include "xicap_priv.h"
+
+
+/* Module arguments */
+unsigned int min_packet_size = (32 * L1_CACHE_BYTES);
+module_param(min_packet_size, uint, 0644);
+
+
+
+/*
+ * Using vmap() to map the buffer led to problems that still
+ * have to be investigated. For now, use a workaround.
+ */
+#define VMAP_WORKAROUND			1
+
+
+
+#define PAGES_PER_FULL_PACKET		7
+#define MAX_PAGES_PER_PACKET		(PAGES_PER_FULL_PACKET + 1)
+#define FULL_PACKET_SIZE		(PAGE_SIZE * PAGES_PER_FULL_PACKET)
+#define ABSOLUTE_MIN_PACKET_SIZE	5
+#define MAX_PACKET_SIZE			32767
+#define DUMMY_PACKET_SIZE		min_packet_size
+
+
+
+/* DMA decriptor-related definitions */
+#define XDMA_RING_SIZE_CODE		3
+#define XDMA_DESC_RING_SIZE		(512 >> XDMA_RING_SIZE_CODE)
+#define XDMA_ENABLE_REGVAL		\
+	(0x80000000 | (XDMA_RING_SIZE_CODE << 16) | (3 << 5))
+
+
+/*
+ * I/O register access macros
+ * Do not use __raw_writeq() and __raw_readq(), these do not seem to work!
+ */
+#define io_writeq(__v__, __a__)	\
+	*(volatile unsigned long long *) (__a__) = (__v__)
+#define io_readq(__a__)		(*(volatile unsigned long long *) (__a__))
+#define io_readl(__a__)		__raw_readl((__a__))
+#define io_writel(__v__, __a__)	__raw_writel((__v__), (__a__))
+#define io_readb(__a__)		__raw_readb((__a__))
+#define io_writeb(__v__, __a__)	__raw_writeb((__v__), (__a__))
+
+
+
+typedef struct __pkt		packet_t;
+typedef struct __gpi_devctxt	xicap_gpi_device_context_t;
+
+
+/* Function prototypes */
+static int __init xicap_gpi_probe(struct device *);
+static int __exit xicap_gpi_remove(struct device *);
+static int xicap_gpi_start(struct device *);
+static void xicap_gpi_stop(struct device *);
+static int xicap_gpi_flush(struct device *);
+static xicap_frame_context_t * xicap_gpi_do_buffer(struct device *, 
xicap_data_buffer_t *);
+#if VMAP_WORKAROUND
+static int xicap_gpi_finish_buffer(struct device *, xicap_frame_context_t *);
+#endif
+static void xicap_gpi_run_pkt_queue(xicap_gpi_device_context_t *);
+static void xicap_gpi_start_data(xicap_gpi_device_context_t *);
+static void xicap_gpi_stop_data(xicap_gpi_device_context_t *);
+static void xicap_gpi_pkt_finish(struct work_struct *);
+static void xicap_gpi_flush_queue(struct list_head *, unsigned int);
+static irqreturn_t xicap_gpi_int_handler(int, void *);
+
+
+
+/* A common name for various objects */
+static const char xicap_gpi_name[] = "xicap_gpi";
+
+
+
+/* The driver struct */
+static struct device_driver xicap_gpi_driver = {
+	.name		= (char *) xicap_gpi_name,
+	.bus		= &platform_bus_type,
+	.owner		= THIS_MODULE,
+	.probe		= xicap_gpi_probe,
+	.remove		= __exit_p(xicap_gpi_remove),
+	.shutdown	= NULL,
+	.suspend	= NULL,
+	.resume		= NULL
+};
+
+
+
+static const xicap_hw_driver_t xicap_gpi_hwdrv = {
+	.start		= xicap_gpi_start,
+	.stop		= xicap_gpi_stop,
+	.do_buffer	= xicap_gpi_do_buffer,
+#if VMAP_WORKAROUND
+	.finish_buffer	= xicap_gpi_finish_buffer,
+#else
+	.finish_buffer	= NULL,
+#endif
+	.flush		= xicap_gpi_flush
+};
+
+
+
+/* A work queue for cleting packets */
+struct workqueue_struct *wq;
+
+
+
+/* A DMA buffer used to work around the RM9K GPI silicon bug */
+unsigned long dummy_dma_buffer;
+
+
+
+/* XDMA read descriptor */
+typedef struct {
+	u64	cpu_part;
+	u64	xdma_part;
+} xdmadesc_t;
+
+
+
+/* A context struct for every device */
+struct __gpi_devctxt {
+	struct semaphore		lock;
+	unsigned int			slice;
+	unsigned int			irq;
+	unsigned int			fifomem_start;
+	unsigned int			fifomem_size;
+
+	void __iomem *			regaddr_fifo_rx;
+	void __iomem *			regaddr_fifo_tx;
+	void __iomem *			regaddr_xdma;
+	void __iomem *			regaddr_pktproc;
+	void __iomem *			regaddr_fpga;
+	void __iomem *			dmadesc;
+
+	dma_addr_t			dmadesc_p;
+	atomic_t			desc_cnt;
+
+	struct list_head		frm_queue;
+	unsigned int			frm_cnt;
+	unsigned int			frm_ready_cnt;
+
+	/* Core driver context pointer */
+	xicap_device_context_t *	devctxt;
+
+	/*
+	 * The interrupt queue, where packes are queued for
+	 * processing at interrupt level.
+	 */
+	spinlock_t			int_queue_lock;
+	struct list_head		int_queue;
+	unsigned int			int_errflg;
+
+
+	/* The packet queue & related stuff */
+	struct list_head		pkt_queue;
+	unsigned int			pkt_page_i;
+
+
+	void __iomem *			curdesc;
+};
+
+
+
+struct __pkt {
+	union {
+		struct list_head	link;
+		struct work_struct	wrk;
+	}			link;
+	dma_addr_t		pgaddr[MAX_PAGES_PER_PACKET];
+	xicap_frame_context_t *	fctxt;
+	volatile const u64 *	desc2;
+	u16			copy_size;
+	u16			mapped_size;
+	u16			remain_size;
+	void *			copy_src;
+	struct page **		copy_pg;
+	unsigned int		copy_offs;
+	struct page **		page;
+	unsigned int 		ndirty;
+};
+
+
+
+#define PKTFLG_FIFO_OVERFLOW	0x01
+#define PKTFLG_DATA_ERROR	0x02
+#define PKTFLG_XDMA_ERROR	0x04
+#define PKTFLG_DESC_UNDERRUN	0x08
+
+
+
+
+struct xicap_frmctxt {
+	struct list_head		link;
+	struct device *			dev;
+	unsigned int			flags;
+	xicap_gpi_device_context_t *	dc;
+	int				status;
+	u16				fpga_data[5];
+	xicap_data_buffer_t *		buf;
+	atomic_t			npkts;
+	unsigned int 			total_pkts;
+	packet_t			pkts[0];
+};
+
+
+
+static inline xicap_data_buffer_t * xicap_gpi_delete_packet(packet_t *pkt)
+{
+	xicap_frame_context_t * const fctxt = pkt->fctxt;
+	xicap_data_buffer_t * res = NULL;
+
+	if (!atomic_dec_return(&fctxt->npkts)) {
+		res = fctxt->buf;
+		res->status = fctxt->status;
+#if !VMAP_WORKAROUND
+		kfree(fctxt);
+#endif
+	}
+
+	return res;
+}
+
+
+
+static inline const struct resource *
+xicap_gpi_get_resource(struct platform_device *d, unsigned long flags,
+		 const char *basename)
+{
+	const char fmt[] = "%s_%u";
+	char buf[80];
+
+	if (unlikely(snprintf(buf, sizeof buf, fmt, basename, d->id) >= sizeof buf))
+		return NULL;
+	return platform_get_resource_byname(d, flags, buf);
+}
+
+
+
+static inline void __iomem *
+xicap_gpi_map_regs(struct platform_device *d, const char *basename)
+{
+	void * result = NULL;
+	const struct resource * const r =
+		xicap_gpi_get_resource(d, IORESOURCE_MEM, basename);
+	if (likely(r))
+		result = ioremap_nocache(r->start, r->end + 1 - r->start);
+	return result;
+}
+
+
+
+/* No hotplugging on the platform bus - use __init */
+static int __init xicap_gpi_probe(struct device *dev)
+{
+	int res;
+	xicap_gpi_device_context_t *dc = NULL;
+	struct platform_device * pdv;
+	const struct resource * rsrc;
+
+	static char __initdata
+		rsrcname_gpi_slice[] = XICAP_RESOURCE_GPI_SLICE,
+		rsrcname_fifo_blk[] = XICAP_RESOURCE_FIFO_BLK,
+		rsrcname_irq[] = XICAP_RESOURCE_IRQ,
+		rsrcname_dmadesc[] = XICAP_RESOURCE_DMADESC,
+		rsrcname_fifo_rx[] = XICAP_RESOURCE_FIFO_RX,
+		rsrcname_fifo_tx[] = XICAP_RESOURCE_FIFO_TX,
+		rsrcname_xdma[] = XICAP_RESOURCE_XDMA,
+		rsrcname_pktproc[] = XICAP_RESOURCE_PKTPROC,
+		rsrcname_pkt_stream[] = XICAP_RESOURCE_PKT_STREAM;
+
+	/* Get the platform device. */
+	if (unlikely(dev->bus != &platform_bus_type)) {
+		res = -ENODEV;
+		goto errex;
+	}
+
+	pdv = to_platform_device(dev);
+
+	/* Create and set up the device context */
+	dc = (xicap_gpi_device_context_t *)
+	      kmalloc(sizeof (xicap_gpi_device_context_t), GFP_KERNEL);
+	if (!dc) {
+		res = -ENOMEM;
+		goto errex;
+	}
+	memset(dc, 0, sizeof *dc);
+	init_MUTEX(&dc->lock);
+
+	/* Evaluate resources */
+	res = -ENXIO;
+
+	rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_gpi_slice);
+	if (unlikely(!rsrc)) goto errex;
+	dc->slice = rsrc->start;
+
+	rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_fifo_blk);
+	if (unlikely(!rsrc)) goto errex;
+	dc->fifomem_start = rsrc->start;
+	dc->fifomem_size = rsrc->end + 1 - rsrc->start;
+
+	rsrc = xicap_gpi_get_resource(pdv, IORESOURCE_IRQ, rsrcname_irq);
+	if (unlikely(!rsrc)) goto errex;
+	dc->irq = rsrc->start;
+
+	rsrc = xicap_gpi_get_resource(pdv, IORESOURCE_MEM, rsrcname_dmadesc);
+	if (unlikely(!rsrc)) goto errex;
+	if (unlikely((rsrc->end + 1 - rsrc->start)
+	             < (XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t))))
+		goto errex;
+	dc->dmadesc_p = (dma_addr_t) rsrc->start;
+	dc->dmadesc = ioremap_nocache(rsrc->start, rsrc->end + 1 - rsrc->start);
+
+	dc->regaddr_fifo_rx = xicap_gpi_map_regs(pdv, rsrcname_fifo_rx);
+	dc->regaddr_fifo_tx = xicap_gpi_map_regs(pdv, rsrcname_fifo_tx);
+	dc->regaddr_xdma = xicap_gpi_map_regs(pdv, rsrcname_xdma);
+	dc->regaddr_pktproc = xicap_gpi_map_regs(pdv, rsrcname_pktproc);
+	dc->regaddr_fpga = xicap_gpi_map_regs(pdv, rsrcname_pkt_stream);
+
+	if (unlikely(!dc->regaddr_fifo_rx || !dc->regaddr_fifo_tx
+	    || !dc->regaddr_xdma || !dc->regaddr_pktproc || !dc->regaddr_fpga
+	    || !dc->dmadesc))
+		goto errex;
+
+	/* Register the device with the core */
+	dc->devctxt = xicap_device_register(dev, &xicap_gpi_hwdrv);
+	res = IS_ERR(dc->devctxt) ? PTR_ERR(dc->devctxt) : 0;
+
+errex:
+	if (res) {
+		if (dc->regaddr_fifo_rx) iounmap(dc->regaddr_fifo_rx);
+		if (dc->regaddr_fifo_tx) iounmap(dc->regaddr_fifo_tx);
+		if (dc->regaddr_xdma) iounmap(dc->regaddr_xdma);
+		if (dc->regaddr_pktproc) iounmap(dc->regaddr_pktproc);
+		if (dc->regaddr_fpga) iounmap(dc->regaddr_fpga);
+		if (dc->dmadesc) iounmap(dc->dmadesc);
+		if (dc) kfree(dc);
+		dev_dbg("%s: %s failed, error = %d\n", xicap_gpi_name,
+			__func__, res);
+	} else {
+		dev->driver_data = dc;
+		dev_dbg("%s: Context at %p\n", xicap_gpi_name, dc);
+	}
+
+	return res;
+}
+
+
+
+static int __exit xicap_gpi_remove(struct device *dev)
+{
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) dev->driver_data;
+
+	xicap_device_unregister(dc->devctxt);
+	kfree(dc);
+	dev->driver_data = NULL;
+	return 0;
+}
+
+
+
+static int xicap_gpi_start(struct device *dev)
+{
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) dev->driver_data;
+	u32 reg;
+	int res;
+
+	/* Lock the device context */
+	down(&dc->lock);
+
+	/* Device context initialization */
+	INIT_LIST_HEAD(&dc->pkt_queue);
+	INIT_LIST_HEAD(&dc->frm_queue);
+	INIT_LIST_HEAD(&dc->int_queue);
+	spin_lock_init(&dc->int_queue_lock);
+	dc->int_errflg = 0;
+
+	lock_titan_regs();
+
+	/* Disable the slice status interrupts */
+	reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+	titan_writel(reg, 0x0050);
+
+	/* Disable the XDMA interrupts for this slice */
+	reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+	titan_writel(reg, 0x0058);
+
+	unlock_titan_regs();
+
+	xicap_gpi_start_data(dc);
+
+	res = request_irq(dc->irq, xicap_gpi_int_handler,
+			  SA_SHIRQ, xicap_gpi_name, dc);
+	if (unlikely(res))
+		return res;
+
+	lock_titan_regs();
+
+	/* Enable the slice status interrupts */
+	reg = titan_readl(0x0050) | (0x2 << (dc->slice * 5));
+	titan_writel(reg, 0x0050);
+
+	/* Enable the XDMA data interrupt */
+	reg = 0xff << (dc->slice * 8);
+	titan_writel(reg, 0x0048);
+	titan_writel(reg, 0x004c);
+	reg = titan_readl(0x0058);
+	titan_writel(reg | (0x1 << (dc->slice * 8)), 0x0058);
+
+	unlock_titan_regs();
+
+	/* Release the device context and exit */
+	up(&dc->lock);
+	return 0;
+}
+
+
+
+static void xicap_gpi_start_data(xicap_gpi_device_context_t *dc)
+{
+	unsigned int i;
+
+	/* Reset all XDMA channels for this slice */
+	io_writel(0x80080000, dc->regaddr_xdma + 0x0000);
+	io_writel(0x80080000, dc->regaddr_xdma + 0x0040);
+	io_writel(0x80080000, dc->regaddr_xdma + 0x0080);
+	io_writel(0x80080000, dc->regaddr_xdma + 0x00c0);
+
+	/* Reset & enable the XDMA slice interrupts */
+	io_writel(0x80068002, dc->regaddr_xdma + 0x000c);
+	io_writel(0x00008002, dc->regaddr_xdma + 0x0010);
+
+	dc->pkt_page_i = 0;
+	dc->frm_ready_cnt = dc->frm_cnt = 0;
+
+	/* Set up the XDMA descriptor ring & enable the XDMA */
+	dc->curdesc = dc->dmadesc;
+	atomic_set(&dc->desc_cnt, XDMA_DESC_RING_SIZE);
+	io_writel(dc->dmadesc_p, dc->regaddr_xdma + 0x0018);
+	wmb();
+	memset(dc->dmadesc, 0, XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t));
+	io_writel(XDMA_ENABLE_REGVAL, dc->regaddr_xdma + 0x0000);
+
+	/*
+	 * Enable the rx fifo we are going to use. Disable the
+	 * unused ones as well as the tx fifo.
+	 */
+	io_writel(0x00100000 | ((dc->fifomem_size) << 10)
+		  | dc->fifomem_start,
+		  dc->regaddr_fifo_rx + 0x0000);
+	wmb();
+	io_writel((10 << 20) | (10 << 10) | 128, dc->regaddr_fifo_rx
+		  + 0x0004);
+	io_writel(0x00100400, dc->regaddr_fifo_rx + 0x000c);
+	io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0018);
+	io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0024);
+	io_writel(0x00100400, dc->regaddr_fifo_tx + 0x0000);
+
+	/* Reset any pending interrupt, then enable fifo */
+	titan_writel(0xf << (dc->slice * 4), 0x482c);
+	wmb();
+	io_writel(0x00200000 | ((dc->fifomem_size) << 10)
+		  | dc->fifomem_start,
+		  dc->regaddr_fifo_rx + 0x0000);
+
+	/* Enable the packet processor */
+	io_writel(0x00000000, dc->regaddr_pktproc + 0x0000);
+	wmb();
+	io_writel(0x0000001f, dc->regaddr_pktproc + 0x0008);
+	io_writel(0x00000e08, dc->regaddr_pktproc + 0x0010);
+	io_writel(0x0000080f, dc->regaddr_pktproc + 0x0014);
+	io_writel(0x000003ff, dc->regaddr_pktproc + 0x0018);
+	io_writel(0x00000100, dc->regaddr_pktproc + 0x0038);
+	wmb();
+	io_writel(0x00000001, dc->regaddr_pktproc + 0x0000);
+
+	/* Disable address filtering */
+	io_writel(0x0, dc->regaddr_pktproc + 0x0120);
+	io_writel(0x2, dc->regaddr_pktproc + 0x0124);
+	for (i = 0; i < 8; i++) {
+		io_writel(  i, dc->regaddr_pktproc + 0x0128);
+		wmb();
+		io_writel(0x0, dc->regaddr_pktproc + 0x0100);
+		io_writel(0x0, dc->regaddr_pktproc + 0x0104);
+		io_writel(0x0, dc->regaddr_pktproc + 0x0108);
+		io_writel(0x0, dc->regaddr_pktproc + 0x010c);
+		wmb();
+	}
+
+	io_writel(0x1, dc->regaddr_pktproc + 0x012c);
+
+}
+
+
+
+static void xicap_gpi_stop_data(xicap_gpi_device_context_t *dc)
+{
+	/* Shut down the data transfer */
+	io_writeb(0x01, dc->regaddr_fpga + 0x000b);
+
+	/* Reset the XDMA channel */
+	io_writel(0x80080000, dc->regaddr_xdma + 0x0000);
+
+	/* Disable the FIFO */
+	io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0000);
+
+	/* Disable the packet processor */
+	io_writel(0x00000000, dc->regaddr_pktproc + 0x0000);
+
+	dc->frm_ready_cnt = 0;
+	INIT_LIST_HEAD(&dc->frm_queue);
+}
+
+
+
+static void xicap_gpi_flush_queue(struct list_head *l, unsigned int stat)
+{
+	while (!list_empty(l)) {
+		packet_t * const pkt =
+			list_entry(l->next, packet_t, link.link);
+		xicap_gpi_device_context_t * const dc = pkt->fctxt->dc;
+		const dma_addr_t *pa = pkt->pgaddr;
+		xicap_data_buffer_t * buf;
+
+		list_del(&pkt->link.link);
+
+		while (pkt->mapped_size) {
+			size_t sz = pkt->mapped_size;
+			if (sz > PAGE_SIZE)
+				sz = PAGE_SIZE;
+			pkt->mapped_size -= sz;
+			dma_unmap_page(pkt->fctxt->dev, *pa++, sz,
+				       DMA_FROM_DEVICE);
+		}
+
+		if (pkt->copy_size) {
+			free_pages((unsigned long) pkt->copy_src,
+				   pkt->copy_size > PAGE_SIZE ? 1 : 0);
+			pkt->copy_size = 0;
+		}
+
+		buf = xicap_gpi_delete_packet(pkt);
+		if (buf) {
+			buf->status = stat;
+			xicap_frame_done(dc->devctxt, buf);
+		}
+	}
+}
+
+
+
+static void xicap_gpi_stop(struct device *dev)
+{
+	u32 reg;
+	LIST_HEAD(l);
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) dev->driver_data;
+
+	lock_titan_regs();
+
+	/* Disable the slice status interrupts */
+	reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+	titan_writel(reg, 0x0050);
+
+	/* Disable the XDMA interrupts for this slice */
+	reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+	titan_writel(reg, 0x0058);
+
+	unlock_titan_regs();
+	flush_workqueue(wq);
+	down(&dc->lock);
+	xicap_gpi_stop_data(dc);
+
+	/* Now clean up the packet & int queues */
+	spin_lock_irq(&dc->int_queue_lock);
+	list_splice_init(&dc->int_queue, &l);
+	spin_unlock_irq(&dc->int_queue_lock);
+	list_splice_init(&dc->pkt_queue, l.prev);
+	xicap_gpi_flush_queue(&l, ~0x0);
+	up(&dc->lock);
+
+	/* Detach interrupt handler */
+	free_irq(dc->irq, dc);
+}
+
+
+
+static int xicap_gpi_flush(struct device *dev)
+{
+	u32 reg;
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) dev->driver_data;
+	LIST_HEAD(l);
+
+	lock_titan_regs();
+
+	/* Disable the slice status interrupts */
+	reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+	titan_writel(reg, 0x0050);
+
+	/* Disable the XDMA interrupts for this slice */
+	reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+	titan_writel(reg, 0x0058);
+
+	unlock_titan_regs();
+
+	/* Now clean up the packet & int queues */
+	flush_workqueue(wq);
+	down(&dc->lock);
+	xicap_gpi_stop_data(dc);
+	spin_lock_irq(&dc->int_queue_lock);
+	list_splice_init(&dc->int_queue, &l);
+	spin_unlock_irq(&dc->int_queue_lock);
+	list_splice_init(&dc->pkt_queue, l.prev);
+	xicap_gpi_flush_queue(&l, XICAP_BUFSTAT_ABORTED);
+	xicap_gpi_start_data(dc);
+	up(&dc->lock);
+
+	lock_titan_regs();
+
+	/* Re-enable the slice status interrupts */
+	reg = titan_readl(0x0050) | (0x2 << (dc->slice * 5));
+	titan_writel(reg, 0x0050);
+
+	/* Re-enable the XDMA data interrupt */
+	reg = 0xff << (dc->slice * 8);
+	titan_writel(reg, 0x0048);
+	titan_writel(reg, 0x004c);
+	reg = titan_readl(0x0058);
+	wmb();
+	titan_writel(reg | (0x1 << (dc->slice * 8)), 0x0058);
+
+	unlock_titan_regs();
+
+	return 0;
+}
+
+
+
+static xicap_frame_context_t *
+xicap_gpi_do_buffer(struct device *dev, xicap_data_buffer_t *buf)
+{
+	unsigned long head_buf = 0, tail_buf = 0;
+	u16 head_size = 0, tail_size = 0, full_size = FULL_PACKET_SIZE;
+	u16 total_packets;
+	size_t sz = buf->size;
+	unsigned int full_packets = 0, head_order = 0, i, request_frame = 1;
+	const unsigned int boffs = (unsigned long) buf->uaddr & ~PAGE_MASK;
+	packet_t *pkt;
+	struct page ** pg;
+	LIST_HEAD(packets);
+	xicap_frame_context_t * fctxt;
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) dev->driver_data;
+
+	if (unlikely(sz < ABSOLUTE_MIN_PACKET_SIZE))
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * If the buffer is not page aligned, the first part of it
+	 * (the 'head') is DMA'ed into a temporary buffer and later
+	 * copied to the user's buffer. The size of the head is chosen
+	 * so that the remaining part of the buffer is page aligned.
+	 */
+	if (boffs) {
+		head_size = PAGE_SIZE - boffs;
+		if (head_size < min_packet_size) {
+			head_size += PAGE_SIZE;
+			head_order = 1;
+		}
+		if (head_size > sz) {
+			head_size = sz;
+			head_order = (head_size > PAGE_SIZE) ? 1 : 0;
+		}
+		head_buf = __get_dma_pages(GFP_KERNEL, head_order);
+		if (!head_buf)
+			return ERR_PTR(-ENOMEM);
+
+		/* Compute the residual buffer size */
+		sz -= head_size;
+	}
+
+	/*
+	 * Now compute the number of full-sized packets, and the size
+	 * of the last ('tail') packet.
+	 */
+	if (sz) {
+		full_packets = sz / FULL_PACKET_SIZE;
+		tail_size = sz % FULL_PACKET_SIZE;
+	}
+
+	/*
+	 * If the tail packet is less than a page, it can be merged with
+	 * the previous one. This also covers the case where the size of
+	 * the tail packet is less than min_packet_size.
+	 */
+	if ((tail_size < PAGE_SIZE) && full_packets) {
+		full_packets--;
+		tail_size += FULL_PACKET_SIZE;
+	}
+
+	/*
+	 * The XDMA will pad the last packet to the next cache line
+	 * boundary, so in order to avoid writing beyond the user's
+	 * buffer, we need to use a temporary buffer if the tail packet
+	 * size is not an integer multiple of the cache line size.
+	 */
+	if (tail_size % L1_CACHE_BYTES) {
+		tail_buf = __get_dma_pages(GFP_KERNEL, 0);
+		if (unlikely(!tail_buf)) {
+			if (head_buf)
+				free_pages(head_buf, head_order);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+
+	/*
+	 * Now we know how many packets to process for the buffer, so we
+	 * can allocate a packet set. Add one extra dummy packet (silicon
+	 * bug workaround).
+	 */
+	total_packets =
+		full_packets + (head_size ? 1 : 0) + (tail_size ? 1 : 0) + 1;
+	fctxt = kmalloc(
+		sizeof (xicap_frame_context_t) + sizeof (packet_t) * total_packets,
+		GFP_KERNEL);
+	if (unlikely(!fctxt)) {
+		if (tail_buf)
+			free_pages(tail_buf, 0);
+		if (head_buf)
+			free_pages(head_buf, head_order);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fctxt->buf = buf;
+	fctxt->dev = dev;
+	fctxt->flags = 0;
+	fctxt->status = XICAP_BUFSTAT_OK;
+	atomic_set(&fctxt->npkts, total_packets);
+	fctxt->total_pkts = total_packets;
+	pkt = &fctxt->pkts[0];
+	pg = buf->pages;
+	fctxt->dc = (xicap_gpi_device_context_t *) dev->driver_data;
+
+	/* Set up the head packet descriptor */
+	if (head_size) {
+		struct page * const p = virt_to_page((void *) head_buf);
+
+		pkt->page = pkt->copy_pg = pg;
+
+		if (head_order) {
+			pkt->pgaddr[0] = dma_map_page(dev, p, 0, PAGE_SIZE,
+						      DMA_FROM_DEVICE);
+			pkt->pgaddr[1] = dma_map_page(dev, p + 1, 0,
+						      head_size - PAGE_SIZE,
+						      DMA_FROM_DEVICE);
+			pkt->ndirty = 2;
+			pg += 2;
+		} else {
+			pkt->pgaddr[0] = dma_map_page(dev, p, 0, head_size,
+						      DMA_FROM_DEVICE);
+			pkt->ndirty = 1;
+			pg++;
+		}
+
+		pkt->copy_src = (void *) head_buf;
+		pkt->copy_offs = ((unsigned long)buf->uaddr & ~PAGE_MASK);
+		pkt->remain_size = pkt->mapped_size = pkt->copy_size = head_size;
+		pkt->fctxt = fctxt;
+		list_add_tail(&pkt->link.link, &packets);
+		pkt++;
+	}
+
+	/* Set up descriptors for all full-sized packets */
+	while (full_packets--) {
+		pkt->remain_size = pkt->mapped_size = FULL_PACKET_SIZE;
+		pkt->copy_size = 0;
+		pkt->page = pg;
+		pkt->ndirty = PAGES_PER_FULL_PACKET;
+
+		for (i = 0; i < PAGES_PER_FULL_PACKET; i++)
+			pkt->pgaddr[i] = dma_map_page(dev, *pg++, 0, PAGE_SIZE,
+						      DMA_FROM_DEVICE);
+
+		pkt->fctxt = fctxt;
+		list_add_tail(&pkt->link.link, &packets);
+		pkt++;
+	}
+
+	/* Set up the descriptor for the tail packet */
+	if (tail_size) {
+		const size_t cs = tail_size % PAGE_SIZE;
+
+		pkt->remain_size = pkt->mapped_size = tail_size;
+		pkt->page = pg;
+		pkt->ndirty = tail_size / PAGE_SIZE;
+
+		for (i = 0; i < pkt->ndirty; i++)
+			pkt->pgaddr[i] = dma_map_page(dev, *pg++, 0, PAGE_SIZE,
+						      DMA_FROM_DEVICE);
+
+		if (cs) {
+			if (tail_buf) {
+				struct page * const p =
+					virt_to_page((void *) tail_buf);
+				pkt->pgaddr[i] =
+					dma_map_page(dev, p, 0, cs,
+						     DMA_FROM_DEVICE);
+				pkt->copy_src = (void *) tail_buf;
+				pkt->copy_size = cs;
+				pkt->copy_pg = pg;
+				pkt->copy_offs = 0;
+			} else {
+				pkt->pgaddr[i] = dma_map_page(dev, *pg, 0, cs,
+							      DMA_FROM_DEVICE);
+				pkt->copy_size = 0;
+			}
+			pkt->ndirty++;
+		} else {
+			pkt->copy_size = 0;
+		}
+
+		pkt->fctxt = fctxt;
+		list_add_tail(&pkt->link.link, &packets);
+		pkt++;
+	}
+
+	/*
+	 * Set up the trailing dummy packet (silicon bug workaround).
+	 *
+	 * The XDMA does not generate an interrupt if the memory
+	 * controlled by the last DMA descriptor is filled with data
+	 * to the last byte. We work around this by adding a dummy packet
+	 * after each frame. This guarantees that there always is an
+	 * active DMA descriptor after the last one of the frame, and so
+	 * the XDMA will interrupt. The dummy packet size is chosen so that
+	 * the dummy buffer is not entirely filled and hence will always
+	 * generate an interrupt, too.
+	 */
+	pkt->remain_size = pkt->mapped_size = DUMMY_PACKET_SIZE;
+	pkt->page = NULL;
+	pkt->copy_size = 0;
+	pkt->ndirty = 0;
+
+	for (i = 0; i < DUMMY_PACKET_SIZE / PAGE_SIZE; i++)
+		pkt->pgaddr[i] =
+			dma_map_page(dev,
+				     virt_to_page((void *) dummy_dma_buffer),
+				     0, PAGE_SIZE, DMA_FROM_DEVICE);
+
+	pkt->pgaddr[i] =
+		dma_map_page(dev,
+			     virt_to_page((void *) dummy_dma_buffer),
+			     0, DUMMY_PACKET_SIZE % PAGE_SIZE,
+			     DMA_FROM_DEVICE);
+
+	pkt->fctxt = fctxt;
+	list_add_tail(&pkt->link.link, &packets);
+
+	/*
+	 * Set up data to send to the FPGA. The total number of packets
+	 * requested does _not_ include the dummy packet. If DUMMY_PACKET_SIZE
+	 * is not zero, a dummy packet is always sent.
+	 */
+	fctxt->fpga_data[0] = cpu_to_le16(fctxt->pkts[0].mapped_size);
+	fctxt->fpga_data[1] = cpu_to_le16(full_size);
+	fctxt->fpga_data[2] = cpu_to_le16((pkt - 1)->mapped_size);
+	fctxt->fpga_data[3] = cpu_to_le16(DUMMY_PACKET_SIZE);
+	fctxt->fpga_data[4] = cpu_to_le16(total_packets - 1);
+
+	down(&dc->lock);
+
+	/* Now enqueue all the packets in one step */
+	list_splice(&packets, dc->pkt_queue.prev);
+	if (!dc->frm_cnt++)
+		xicap_gpi_run_pkt_queue(dc);
+
+	dev_dbg("%s: created packet set %p\n"
+		"\thead size = %#06x, full size = %#06x, "
+		"tail size = %#06x, dummy size = %#06x\n"
+		"\ttotal packets = %u, active DMA descriptors = %u\n",
+		xicap_gpi_name, fctxt, head_size, full_size, tail_size,
+		DUMMY_PACKET_SIZE, total_packets,
+		io_readl(dc->regaddr_xdma + 0x0008));
+
+	/*
+	 * If less than two frames are currently pending, we can send
+	 * the frame parameters to the FPGA right away. Otherwise, we
+	 * need to queue the frame.
+	 */
+	if (dc->frm_ready_cnt++ > 1) {
+		list_add_tail(&fctxt->link, &dc->frm_queue);
+		request_frame = 0;
+	}
+
+	up(&dc->lock);
+
+	if (request_frame)
+		memcpy_toio(dc->regaddr_fpga, fctxt->fpga_data,
+			    sizeof fctxt->fpga_data);
+
+	return fctxt;
+}
+
+
+
+static void xicap_gpi_run_pkt_queue(xicap_gpi_device_context_t *dc)
+{
+	packet_t *pkt = list_empty(&dc->pkt_queue) ? NULL :
+			list_entry(dc->pkt_queue.next, packet_t, link.link);
+
+	while (pkt) {
+		int i;
+		size_t sz;
+
+		/* Stop, if no more free DMA descriptors */
+		if (0 > atomic_dec_if_positive(&dc->desc_cnt))
+			break;
+
+		i = dc->pkt_page_i++;
+
+		sz = pkt->remain_size;
+		if (sz > PAGE_SIZE)
+			sz = PAGE_SIZE;
+		pkt->remain_size -= sz;
+
+		/* Set up the DMA descriptor */
+		io_writeq(cpu_to_be64(pkt->pgaddr[i]), dc->curdesc);
+		if (i) {
+			io_writeq(cpu_to_be64(0x1ULL << 53), dc->curdesc + 8);
+		} else {
+			io_writeq(cpu_to_be64((0x1ULL << 63) | (0x1ULL << 53)),
+				  dc->curdesc + 8);
+			pkt->desc2 = dc->curdesc + 8;
+		}
+
+		dev_dbg("%s: Desc. %2u = %016Lx, %016Lx\n",
+			xicap_gpi_name,
+			(dc->curdesc - dc->dmadesc) / sizeof (xdmadesc_t),
+			(u64) pkt->pgaddr[i], pkt->desc2);
+
+		dc->curdesc += sizeof (xdmadesc_t);
+		if ((dc->curdesc - dc->dmadesc) >=
+		    (XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t)))
+			dc->curdesc = dc->dmadesc;
+
+		/* Add the packet to the interrupt queue */
+		if (!pkt->remain_size) {
+			spin_lock_irq(&dc->int_queue_lock);
+			list_move_tail(&pkt->link.link, &dc->int_queue);
+			spin_unlock_irq(&dc->int_queue_lock);
+			dc->pkt_page_i = 0;
+			pkt = list_empty(&dc->pkt_queue) ? NULL :
+			      list_entry(dc->pkt_queue.next, packet_t,
+					 link.link);
+		}
+
+		io_writel(1, dc->regaddr_xdma + 0x0008);
+	}
+}
+
+
+#if VMAP_WORKAROUND
+static int
+xicap_gpi_finish_buffer(struct device *dev, xicap_frame_context_t *frmctxt)
+{
+	struct __pkt * const pkt_h = frmctxt->pkts,
+		     * const pkt_t = frmctxt->pkts + frmctxt->total_pkts - 2;
+
+	if (pkt_h->copy_size) {
+		__copy_to_user(frmctxt->buf->uaddr, pkt_h->copy_src,
+			       pkt_h->copy_size);
+		free_pages((unsigned long) pkt_h->copy_src,
+			   (pkt_h->copy_size > PAGE_SIZE) ? 1 : 0);
+	}
+
+	if (2 < frmctxt->total_pkts) {
+		if (pkt_t->copy_size) {
+			__copy_to_user(frmctxt->buf->uaddr + frmctxt->buf->size
+				       - pkt_t->copy_size,
+				       pkt_t->copy_src,
+				       pkt_t->copy_size);
+			free_pages((unsigned long) pkt_t->copy_src, 0);
+		}
+	}
+
+	kfree(frmctxt);
+	return 0;
+}
+#endif
+
+
+
+static void xicap_gpi_pkt_finish(struct work_struct *work)
+{
+	packet_t * const pkt = container_of(work, packet_t, link.wrk);
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) pkt->fctxt->dev->driver_data;
+	const dma_addr_t *pa = pkt->pgaddr;
+	xicap_data_buffer_t * buf;
+
+	while (pkt->mapped_size) {
+		size_t sz = pkt->mapped_size;
+		if (sz > PAGE_SIZE)
+			sz = PAGE_SIZE;
+		pkt->mapped_size -= sz;
+		dma_unmap_page(pkt->fctxt->dev, *pa++, sz, DMA_FROM_DEVICE);
+	}
+
+#if !VMAP_WORKAROUND
+	if (pkt->copy_size) {
+		const unsigned int page_order =
+			(pkt->copy_size > PAGE_SIZE) ? 1 : 0;
+		void * const dst = vmap(pkt->copy_pg, 0x1 << page_order,
+					VM_MAP, PAGE_USERIO);
+
+		if (dst) {
+			memcpy(dst + pkt->copy_offs, pkt->copy_src,
+			       pkt->copy_size);
+			free_pages((unsigned long) pkt->copy_src, page_order);
+			vunmap(dst);
+		} else {
+			pkt->fctxt->status = XICAP_BUFSTAT_VMERR;
+		}
+	}
+#endif
+
+	while (pkt->ndirty--)
+		set_page_dirty_lock(*pkt->page++);
+
+	down(&dc->lock);
+	buf = xicap_gpi_delete_packet(pkt);
+	if (buf) {
+		xicap_frame_context_t *fctxt = NULL;
+		xicap_frame_done(dc->devctxt, buf);
+		dc->frm_cnt--;
+		if (dc->frm_ready_cnt-- > 2) {
+			fctxt = list_entry(dc->frm_queue.next,
+					  xicap_frame_context_t, link);
+			list_del(&fctxt->link);
+		}
+
+		if (fctxt)
+			memcpy_toio(dc->regaddr_fpga, fctxt->fpga_data,
+				    sizeof fctxt->fpga_data);
+	}
+
+	if (dc->frm_cnt)
+		xicap_gpi_run_pkt_queue(dc);
+	up(&dc->lock);
+}
+
+
+
+/* The interrupt handler */
+static irqreturn_t xicap_gpi_int_handler(int irq, void *arg)
+{
+	xicap_gpi_device_context_t * const dc =
+		(xicap_gpi_device_context_t *) arg;
+	u32 flg_dmadata, flg_slstat, flg_fofl, reg;
+
+	/* Check, if this interrupt is for us */
+	flg_dmadata = titan_readl(0x00