From d9e27abf78b3bd714eb9c37c529d0bff7c5403d9 Mon Sep 17 00:00:00 2001
From: Andrey Volkov <avolkov@varma-el.com>
Date: Fri, 18 Aug 2006 10:01:38 -0600
Subject: [PATCH] [PATCH 01/02] MPC5200 Bestcomm platform driver

This is first part of "platformizied" bestcomm/fec drivers.

Signed-Off-By: Andrey Volkov <avolkov at varma-el.com>
---
 arch/ppc/Kconfig                            |    5 +
 arch/ppc/syslib/Makefile                    |    1 +
 arch/ppc/syslib/bestcomm/Makefile           |    2 +
 arch/ppc/syslib/bestcomm/bestcomm.c         |  408 +++++++++++++++++++++++
 arch/ppc/syslib/bestcomm/bestcomm.h         |  473 +++++++++++++++++++++++++++
 arch/ppc/syslib/bestcomm/fec.c              |  174 ++++++++++
 arch/ppc/syslib/bestcomm/fec.h              |   71 ++++
 arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c |   71 ++++
 arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c |   84 +++++
 arch/ppc/syslib/mpc52xx_devices.c           |   22 ++
 arch/ppc/syslib/mpc52xx_sys.c               |    4 +-
 include/asm-ppc/mpc52xx.h                   |    2 +
 12 files changed, 1315 insertions(+), 2 deletions(-)

diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig
index 8eb82ef..d8a561e 100644
--- a/arch/ppc/Kconfig
+++ b/arch/ppc/Kconfig
@@ -775,6 +775,11 @@ config EMBEDDEDBOOT
 config PPC_MPC52xx
 	bool
 
+config PPC_BESTCOMM
+	bool
+	depends on PPC_MPC52xx
+	default y
+
 config 8260
 	bool "CPM2 Support" if WILLOW
 	depends on 6xx
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile
index dca23f2..ab015df 100644
--- a/arch/ppc/syslib/Makefile
+++ b/arch/ppc/syslib/Makefile
@@ -106,3 +106,4 @@ obj-$(CONFIG_PCI)		+= mpc52xx_pci.o
 endif
 
 obj-$(CONFIG_PPC_I8259)		+= i8259.o
+obj-$(CONFIG_PPC_BESTCOMM)	+= bestcomm/
diff --git a/arch/ppc/syslib/bestcomm/Makefile b/arch/ppc/syslib/bestcomm/Makefile
new file mode 100644
index 0000000..02cc99a
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_PPC_BESTCOMM) 	+= bestcomm.o
+obj-$(CONFIG_FEC_MPC52xx)	+= sdma_fec_rx_task.o sdma_fec_tx_task.o fec.o
diff --git a/arch/ppc/syslib/bestcomm/bestcomm.c b/arch/ppc/syslib/bestcomm/bestcomm.c
new file mode 100644
index 0000000..ef45e02
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/bestcomm.c
@@ -0,0 +1,408 @@
+/*
+ * arch/ppc/syslib/bestcomm/bestcomm.c
+ *
+ * Driver for MPC52xx processor BestComm peripheral controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * HISTORY:
+ *
+ * 2005-08-14	Converted to platform driver by 
+ *		Andrey Volkov <avolkov@varma-el.com>, Varma Electronics Oy
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <asm/bug.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+#include "bestcomm.h"
+
+#define DRIVER_NAME		"mpc52xx-sdma"
+
+struct sdma_io sdma;
+
+static spinlock_t sdma_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_BESTCOMM_DEBUG
+void sdma_dump(void)
+{
+	int i;
+	printk("** SDMA registers: pa = %08x, va = %08x\n", sdma.base_reg_addr, sdma.io);
+	printk("**  taskBar = %08x\n", sdma.io->taskBar);
+	printk("**  currentPointer = %08x\n", sdma.io->currentPointer);
+	printk("**  endPointer = %08x\n", sdma.io->endPointer);
+	printk("**  variablePointer = %08x\n", sdma.io->variablePointer);
+
+	printk("**  IntVect1 = %08x\n", sdma.io->IntVect1);
+	printk("**  IntVect2 = %08x\n", sdma.io->IntVect2);
+	printk("**  PtdCntrl = %08x\n", sdma.io->PtdCntrl);
+
+	printk("**  IntPend = %08x\n", sdma.io->IntPend);
+	printk("**  IntMask = %08x\n", sdma.io->IntMask);
+
+	printk("**  TCR dump:");	
+
+	for (i=0;i<16;i++)  {
+		if(i%8 == 0)
+			printk("\n**   %02X:",i);
+		printk(" %04X",sdma.io->tcr[i]);
+	}
+	printk("\n**  IPR dump:");	
+	for (i=0;i<32;i++)  {
+		if(i%16 == 0)
+			printk("\n**   %02X:",i);
+		printk(" %02X",sdma.io->ipr[i]);
+	}
+	printk("\n**  cReqSelect = %08x\n", sdma.io->cReqSelect);
+	printk("**  task_size0 = %08x\n", sdma.io->task_size0);
+	printk("**  task_size1 = %08x\n", sdma.io->task_size1);
+	printk("**  MDEDebug = %08x\n", sdma.io->MDEDebug);
+	printk("**  ADSDebug = %08x\n", sdma.io->ADSDebug);
+	printk("**  Value1 = %08x\n", sdma.io->Value1);
+	printk("**  Value2 = %08x\n", sdma.io->Value2);
+	printk("**  Control = %08x\n", sdma.io->Control);
+	printk("**  Status = %08x\n", sdma.io->Status);
+	printk("**  PTDDebug = %08x\n", sdma.io->PTDDebug);
+}
+#endif
+
+#ifdef CONFIG_BESTCOMM_DEBUG
+#define SDMA_DUMP_REGS()	sdma_dump()
+#else
+#define SDMA_DUMP_REGS()
+#endif
+
+/*
+ * Use a very simple SRAM allocator.
+ * There is no mechanism for freeing space.
+ * In an attempt to minimize internal fragmentation, the SRAM is
+ * divided into two areas.
+ *
+ * Area 1 is at the beginning of SRAM
+ * and is used for allocations requiring alignments of 16 bytes or less.
+ * Successive allocations return higher addresses.
+ *
+ * Area 2 is at the end of SRAM and is used for the remaining allocations.
+ * Successive allocations return lower addresses.
+ *
+ * I've considered adding routines to support the freeing of SRAM allocations,
+ * but the SRAM is so small (16K) that fragmentation can quickly cause the
+ * SRAM to be unusable.  If you can come up with a slick way to free SRAM
+ * memory without the fragmentation problem, please do so.
+ */
+
+static u8 *area1_end;
+static u8 *area2_begin;
+
+void *sdma_sram_alloc(int size, int alignment, u32 *dma_handle)
+{
+	u8 *a;
+
+	spin_lock(&sdma_lock);
+
+	/* alignment must be a power of 2 */
+	BUG_ON(alignment & (alignment - 1));
+
+	if (alignment < 16) {
+		a = (u8 *)(((u32)area1_end + (alignment-1)) & ~(alignment-1));
+		if (a + size <= area2_begin)
+			area1_end = a + size;
+		else
+			a = 0;				/* out of memory */
+	} else {
+		a = (u8 *)(((u32)area2_begin - size) & ~(alignment - 1));
+		if (a >= area1_end)
+			area2_begin = a;
+		else
+			a = 0;				/* out of memory */
+	}
+	if(a && dma_handle)
+		*dma_handle = sdma_sram_pa(a);
+	spin_unlock(&sdma_lock);
+	return (void *)a;
+}
+
+/* this will need to be updated if Freescale changes their task code FDT */
+static u32 fdt_ops[] = {
+	0xa0045670,	/* FDT[48] */
+	0x80045670,	/* FDT[49] */
+	0x21800000,	/* FDT[50] */
+	0x21e00000,	/* FDT[51] */
+	0x21500000,	/* FDT[52] */
+	0x21400000,	/* FDT[53] */
+	0x21500000,	/* FDT[54] */
+	0x20400000,	/* FDT[55] */
+	0x20500000,	/* FDT[56] */
+	0x20800000,	/* FDT[57] */
+	0x20a00000,	/* FDT[58] */
+	0xc0170000,	/* FDT[59] */
+	0xc0145670,	/* FDT[60] */
+	0xc0345670,	/* FDT[61] */
+	0xa0076540,	/* FDT[62] */
+	0xa0000760,	/* FDT[63] */
+};
+
+static int new_task_number(void)
+{
+	struct sdma_tdt *tdt;
+	int i;
+
+	spin_lock(&sdma_lock);
+
+	tdt = sdma.tdt;
+	for (i=0; i<SDMA_MAX_TASKS; i++, tdt++)
+		if (tdt->start == 0)
+			break;
+	if (i == SDMA_MAX_TASKS)
+		i = -1;
+
+	spin_unlock(&sdma_lock);
+
+	return i;
+}
+
+int sdma_load_task(u32 *task_image)
+{
+	struct sdma_task_header *head = (struct sdma_task_header *)task_image;
+	struct sdma_tdt *tdt;
+	int tasknum;
+	u32 *desc;
+	u32 *var_src, *var_dst;
+	u32 *inc_src;
+	void *start;
+
+	BUG_ON(head->magic != SDMA_TASK_MAGIC);
+
+	tasknum = new_task_number();
+	if (tasknum < 0)
+		return -ENOMEM;
+
+	desc = (u32 *)(head + 1);
+	var_src = desc + head->desc_size;
+	inc_src = var_src + head->var_size;
+
+	tdt = &sdma.tdt[tasknum];
+
+	start = sdma_sram_alloc(head->desc_size * sizeof(u32), 4, &tdt->start);
+	if (!start)
+		return -ENOMEM;
+	tdt->stop = tdt->start + (head->desc_size - 1)*sizeof(u32);
+	var_dst = sdma_sram_va(tdt->var);
+
+	memcpy(start, desc, head->desc_size * sizeof(u32));
+	memcpy(&var_dst[head->first_var], var_src, head->var_size * sizeof(u32));
+	memcpy(&var_dst[SDMA_MAX_VAR], inc_src, head->inc_size * sizeof(u32));
+
+	return tasknum;
+}
+
+void sdma_set_initiator(int task, int initiator)
+{
+	int i;
+	int num_descs;
+	u32 *desc;
+	int next_drd_has_initiator;
+
+	sdma_set_tcr_initiator(task, initiator);
+
+	desc = sdma_task_desc(task);
+	next_drd_has_initiator = 1;
+	num_descs = sdma_task_num_descs(task);
+
+	for (i=0; i<num_descs; i++, desc++) {
+		if (!sdma_desc_is_drd(*desc))
+			continue;
+		if (next_drd_has_initiator)
+			if (sdma_desc_initiator(*desc) != SDMA_INITIATOR_ALWAYS)
+				sdma_set_desc_initiator(desc, initiator);
+		next_drd_has_initiator = !sdma_drd_is_extended(*desc);
+	}
+}
+
+struct sdma *sdma_alloc(int queue_size)
+{
+	struct sdma *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	void **cookie;
+
+	if (!s)
+		return NULL;
+
+	memset(s, 0, sizeof(*s));
+
+	if (queue_size) {
+		cookie = kmalloc(sizeof(*cookie) * queue_size, GFP_KERNEL);
+		if (!cookie) {
+			kfree(s);
+			return NULL;
+		}
+		s->cookie = cookie;
+	}
+
+	s->num_bd = queue_size;
+	return s;
+}
+
+void sdma_free(struct sdma *s)
+{
+	if (s->cookie)
+		kfree(s->cookie);
+	kfree(s);
+}
+
+static int __devinit mpc52xx_sdma_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	int task;
+	u32 *context;
+	u32 *fdt;
+	struct sdma_tdt *tdt;
+	struct resource *mem_io, *mem_sram;
+	u32 tdt_pa, var_pa, context_pa, fdt_pa; 
+	int ret = -ENODEV;
+
+	mem_io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem_sram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem_io || !mem_sram)
+		goto out;
+
+	if (!request_mem_region(mem_io->start, mem_io->end - mem_io->start + 1, DRIVER_NAME)) {
+		printk(KERN_ERR DRIVER_NAME " - resource unavailable\n");
+		goto out;
+	}
+	sdma.base_reg_addr = mem_io->start;
+
+	sdma.io = ioremap_nocache(mem_io->start, sizeof(struct mpc52xx_sdma));
+
+	if (!sdma.io ) {
+		printk(KERN_ERR DRIVER_NAME " - failed to map sdma regs\n");
+		ret = -ENOMEM;
+		goto map_io_error;
+	}
+
+	SDMA_DUMP_REGS();
+
+	sdma.sram_size = mem_sram->end - mem_sram->start + 1;
+	if (!request_mem_region(mem_sram->start, sdma.sram_size, DRIVER_NAME)) {
+		printk(KERN_ERR DRIVER_NAME " - resource unavailable\n");
+		goto req_sram_error;
+	}
+
+	sdma.base_sram_addr = mem_sram->start;
+	sdma.sram = ioremap_nocache(mem_sram->start, sdma.sram_size);
+	if (!sdma.sram ) {
+		printk(KERN_ERR DRIVER_NAME " - failed to map sdma sram\n");
+		ret = -ENOMEM;
+		goto map_sram_error;
+	}
+
+	area1_end = sdma.sram;
+	area2_begin = area1_end + sdma.sram_size;
+
+	memset(area1_end, 0, sdma.sram_size);
+
+	/* allocate space for task descriptors, contexts, and var tables */
+	sdma.tdt = sdma_sram_alloc(sizeof(struct sdma_tdt) * SDMA_MAX_TASKS, 4, &tdt_pa);
+
+	context = sdma_sram_alloc(SDMA_CONTEXT_SIZE * SDMA_MAX_TASKS,
+							  SDMA_CONTEXT_ALIGN, &context_pa);
+	sdma.var = sdma_sram_alloc( (SDMA_VAR_SIZE + SDMA_INC_SIZE) * SDMA_MAX_TASKS, 
+								SDMA_VAR_ALIGN, &var_pa);
+	fdt = sdma_sram_alloc(SDMA_FDT_SIZE, SDMA_FDT_ALIGN, &fdt_pa);
+	memcpy(&fdt[48], fdt_ops, sizeof(fdt_ops));
+
+	out_be32(&sdma.io->taskBar, tdt_pa);
+
+	tdt = sdma.tdt;
+	for (task=0; task < SDMA_MAX_TASKS; task++) {
+		out_be16(&sdma.io->tcr[task], 0);
+		out_8(&sdma.io->ipr[task], 0);
+
+		tdt->context = context_pa;
+		tdt->var = var_pa;
+		tdt->fdt = fdt_pa;
+		var_pa += (SDMA_MAX_VAR + SDMA_MAX_INC)*sizeof(u32);
+		context_pa += SDMA_MAX_CONTEXT*sizeof(u32);
+		tdt++;
+	}
+
+	out_8(&sdma.io->ipr[SDMA_INITIATOR_ALWAYS], SDMA_IPR_ALWAYS);
+
+	/* Disable COMM Bus Prefetch, apparently it's not reliable yet */
+	out_be16(&sdma.io->PtdCntrl, in_be16(&sdma.io->PtdCntrl) | 1);
+
+	printk(KERN_INFO "MPC52xx BestComm inited\n");
+
+	return 0;
+
+map_sram_error:
+	release_mem_region(mem_sram->start, sdma.sram_size);
+req_sram_error:
+	iounmap(sdma.io);
+map_io_error:
+	release_mem_region(mem_io->start, mem_io->end - mem_io->start + 1);
+out:
+	printk(KERN_ERR "DMA: MPC52xx BestComm init FAILED !!!\n");
+	return ret;
+}
+
+
+static struct device_driver mpc52xx_sdma_driver = {
+	.owner	  = THIS_MODULE,
+	.name	  = DRIVER_NAME,
+	.bus	  = &platform_bus_type,
+	.probe 	  = mpc52xx_sdma_probe,
+/*	.remove	  = mpc52xx_sdma_remove,	TODO */
+#ifdef CONFIG_PM
+/*	.suspend	= mpc52xx_sdma_suspend,	TODO */
+/*	.resume		= mpc52xx_sdma_resume,	TODO */
+#endif
+};
+
+static int __init
+mpc52xx_sdma_init(void)
+{
+	printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
+	return driver_register(&mpc52xx_sdma_driver);
+}
+
+#ifdef MODULE
+static void __exit
+mpc52xx_sdma_exit(void)
+{
+	driver_unregister(&mpc52xx_sdma_driver);
+}
+#endif
+
+#ifndef MODULE
+ subsys_initcall(mpc52xx_sdma_init);
+#else
+ module_init(mpc52xx_sdma_init);
+ module_exit(mpc52xx_sdma_exit);
+#endif
+
+
+MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sdma_sram_alloc);
+EXPORT_SYMBOL(sdma_load_task);
+EXPORT_SYMBOL(sdma_set_initiator);
+EXPORT_SYMBOL(sdma_free);
+EXPORT_SYMBOL(sdma);
+
+
diff --git a/arch/ppc/syslib/bestcomm/bestcomm.h b/arch/ppc/syslib/bestcomm/bestcomm.h
new file mode 100644
index 0000000..14bf397
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/bestcomm.h
@@ -0,0 +1,473 @@
+/*
+ * arch/ppc/syslib/bestcomm/bestcomm.h
+ *
+ * Driver for MPC52xx processor BestComm peripheral controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * HISTORY:
+ *
+ * 2005-08-14	Converted to platform driver by 
+ *		Andrey Volkov <avolkov@varma-el.com>, Varma Electronics Oy
+ */
+
+#ifndef __BESTCOMM_BESTCOMM_H__
+#define __BESTCOMM_BESTCOMM_H__
+
+/* Buffer Descriptor definitions */
+struct sdma_bd {
+	u32 status;
+	void *data;
+};
+
+struct sdma_bd2 {
+	u32 status;
+	void *data1;
+	void *data2;
+};
+
+struct sdma_io {
+	unsigned long			base_reg_addr;
+	struct mpc52xx_sdma __iomem	*io;
+	unsigned long			base_sram_addr;
+	void __iomem			*sram;
+	size_t				sram_size;
+
+	struct sdma_tdt __iomem  	*tdt;
+	u32 __iomem			*var;
+};
+extern struct sdma_io sdma;
+
+#define	sdma_sram_pa(virt)	(((unsigned long)(((void __iomem *)(virt))-sdma.sram))+sdma.base_sram_addr)
+#define	sdma_sram_va(pa)	((void __iomem *)((((unsigned long)(pa))-sdma.base_sram_addr)+((unsigned long)sdma.sram)))
+
+#define	sdma_io_pa(virt)	(((unsigned long)(((void __iomem *)(virt))-((void __iomem *)sdma.io)))+sdma.base_reg_addr)
+#define	sdma_io_va(pa)	((void __iomem *)((((unsigned long)(pa))-sdma.base_reg_addr)+((unsigned long)sdma.io)))
+
+#define SDMA_LEN_BITS		26
+#define SDMA_LEN_MASK		((1 << SDMA_LEN_BITS) - 1)
+
+#define SDMA_BD_READY		0x40000000UL
+
+#define SDMA_FEC_TX_BD_TFD	0x08000000UL	/* transmit frame done */
+#define SDMA_FEC_TX_BD_INT	0x04000000UL	/* Interrupt */
+#define SDMA_FEC_TX_BD_TFD_INIT	(SDMA_BD_READY | SDMA_FEC_TX_BD_TFD | \
+							SDMA_FEC_TX_BD_INT)
+
+struct sdma {
+	union {
+		struct sdma_bd *bd;
+		struct sdma_bd2 *bd2;
+	};
+	void **cookie;
+	u16 index;
+	u16 outdex;
+	u16 num_bd;
+	s16 tasknum;
+	u32 flags;
+};
+
+#define SDMA_FLAGS_NONE		0x0000
+#define SDMA_FLAGS_ENABLE_TASK	0x0001
+#define SDMA_FLAGS_BD2		0x0002
+
+/* Task Descriptor Table Entry */
+struct sdma_tdt {
+	u32 start;
+	u32 stop;
+	u32 var;
+	u32 fdt;
+	u32 exec_status; /* used internally by SmartComm engine */
+	u32 mvtp;		 /* used internally by SmartComm engine */
+	u32 context;
+	u32 litbase;
+};
+
+//extern struct sdma_tdt *sdma_tdt;
+
+#define SDMA_MAX_TASKS		16
+#define SDMA_MAX_VAR		24
+#define SDMA_MAX_INC		8
+#define SDMA_MAX_FDT		64
+#define SDMA_MAX_CONTEXT	20
+#define SDMA_CONTEXT_SIZE	SDMA_MAX_CONTEXT * sizeof(u32)
+#define SDMA_CONTEXT_ALIGN	0x100
+#define SDMA_VAR_SIZE		SDMA_MAX_VAR * sizeof(u32)
+#define SDMA_VAR_ALIGN		0x80
+#define SDMA_INC_SIZE		SDMA_MAX_INC * sizeof(u32)
+#define SDMA_FDT_SIZE		SDMA_MAX_FDT * sizeof(u32)
+#define SDMA_FDT_ALIGN		0x100
+#define SDMA_BD_ALIGN		0x10
+
+#define TASK_ENABLE		0x8000
+
+#ifndef DPRINK
+	#ifdef CONFIG_BESTCOMM_DEBUG
+	#define DPRINTK(a,b...)	printk(KERN_DEBUG "sdma: %s: " a, __FUNCTION__ , ## b)
+	#else
+	#define DPRINTK(a,b...)
+	#endif
+#endif
+
+static inline void sdma_enable_task(int task)
+{
+	DPRINTK("***DMA enable task (%d): tdt = %08x\n",task, sdma.tdt);
+	DPRINTK("***tdt->start   = %08x\n",sdma.tdt[task].start);
+	DPRINTK("***tdt->stop    = %08x\n",sdma.tdt[task].stop);
+	DPRINTK("***tdt->var     = %08x\n",sdma.tdt[task].var);
+	DPRINTK("***tdt->fdt     = %08x\n",sdma.tdt[task].fdt);
+	DPRINTK("***tdt->status  = %08x\n",sdma.tdt[task].exec_status);
+	DPRINTK("***tdt->mvtp    = %08x\n",sdma.tdt[task].mvtp);
+	DPRINTK("***tdt->context = %08x\n",sdma.tdt[task].context);
+	DPRINTK("***tdt->litbase = %08x\n",sdma.tdt[task].litbase);
+	DPRINTK("***--------------\n");
+
+	u16 reg = in_be16(&sdma.io->tcr[task]);
+	DPRINTK("***enable task: &sdma.io->tcr=%08x, reg = %04x\n", &sdma.io->tcr, reg);
+	out_be16(&sdma.io->tcr[task],  reg | TASK_ENABLE);
+}
+
+static inline void sdma_disable_task(int task)
+{
+	u16 reg = in_be16(&sdma.io->tcr[task]);
+	DPRINTK("***disable task(%d): reg = %04x\n", task, reg);
+	out_be16(&sdma.io->tcr[task], reg & ~TASK_ENABLE);
+}
+
+static inline int sdma_irq(struct sdma *s)
+{
+	return MPC52xx_SDMA_IRQ_BASE + s->tasknum;
+}
+
+static inline void sdma_enable(struct sdma *s)
+{
+	sdma_enable_task(s->tasknum);
+}
+
+static inline void sdma_disable(struct sdma *s)
+{
+	sdma_disable_task(s->tasknum);
+}
+
+static inline int sdma_queue_empty(struct sdma *s)
+{
+	return s->index == s->outdex;
+}
+
+static inline void sdma_clear_irq(struct sdma *s)
+{
+	out_be32(&sdma.io->IntPend, 1 << s->tasknum);
+}
+
+static inline int sdma_next_index(struct sdma *s)
+{
+	return ((s->index + 1) == s->num_bd) ? 0 : s->index + 1;
+}
+
+static inline int sdma_next_outdex(struct sdma *s)
+{
+	return ((s->outdex + 1) == s->num_bd) ? 0 : s->outdex + 1;
+}
+
+static inline int sdma_queue_full(struct sdma *s)
+{
+	return s->outdex == sdma_next_index(s);
+}
+
+static inline int sdma_buffer_done(struct sdma *s)
+{
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(s->flags & SDMA_FLAGS_BD2);
+#endif
+	if (sdma_queue_empty(s))
+		return 0;
+	return (s->bd[s->outdex].status & SDMA_BD_READY) == 0;
+}
+
+static inline int sdma_buffer2_done(struct sdma *s)
+{
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(!(s->flags & SDMA_FLAGS_BD2));
+#endif
+	if (sdma_queue_empty(s))
+		return 0;
+
+	return (s->bd2[s->outdex].status & SDMA_BD_READY) == 0;
+}
+
+static inline u32 *sdma_task_desc(int task)
+{
+	return sdma_sram_va(sdma.tdt[task].start);
+}
+
+static inline u32 sdma_task_num_descs(int task)
+{
+	return (sdma.tdt[task].stop - sdma.tdt[task].start)/sizeof(u32) + 1;
+}
+
+static inline u32 *sdma_task_var(int task)
+{
+	return sdma_sram_va(sdma.tdt[task].var);
+}
+
+static inline u32 *sdma_task_inc(int task)
+{
+	return &sdma_task_var(task)[SDMA_MAX_VAR];
+}
+
+static inline void sdma_set_tcr_initiator(int task, int initiator) {
+	u16 *tcr = &sdma.io->tcr[task];
+	out_be16(tcr, (in_be16(tcr) & ~0x1f00) | (initiator << 8));
+}
+
+#define SDMA_DRD_INITIATOR_SHIFT	21
+
+static inline int sdma_desc_initiator(u32 desc)
+{
+	return (desc >> SDMA_DRD_INITIATOR_SHIFT) & 0x1f;
+}
+
+static inline void sdma_set_desc_initiator(u32 *desc, int initiator)
+{
+	*desc = (*desc & ~(0x1f << SDMA_DRD_INITIATOR_SHIFT)) |
+			((initiator << SDMA_DRD_INITIATOR_SHIFT) & 0x1f);
+}
+
+static inline void sdma_submit_buffer(struct sdma *s, void *cookie, void *data,
+								int length)
+{
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(s->flags & SDMA_FLAGS_BD2);
+#endif
+	s->cookie[s->index] = cookie;
+	s->bd[s->index].data = data;
+	s->bd[s->index].status = SDMA_BD_READY | length;
+	s->index = sdma_next_index(s);
+	if (s->flags & SDMA_FLAGS_ENABLE_TASK)
+		sdma_enable_task(s->tasknum);
+}
+
+/*
+ * Special submit_buffer function to submit last buffer of a frame to
+ * the FEC tx task.  tfd means "transmit frame done".
+ */
+static inline void sdma_fec_tfd_submit_buffer(struct sdma *s, void *cookie,
+							void *data, int length)
+{
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(s->flags & SDMA_FLAGS_BD2);
+#endif
+	s->cookie[s->index] = cookie;
+	s->bd[s->index].data = data;
+	s->bd[s->index].status = SDMA_FEC_TX_BD_TFD_INIT | length;
+	s->index = sdma_next_index(s);
+	sdma_enable_task(s->tasknum);
+}
+
+static inline void *sdma_retrieve_buffer(struct sdma *s, int *length)
+{
+	void *cookie = s->cookie[s->outdex];
+
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(s->flags & SDMA_FLAGS_BD2);
+#endif
+	if (length)
+		*length = s->bd[s->outdex].status & SDMA_LEN_MASK;
+	s->outdex = sdma_next_outdex(s);
+	return cookie;
+}
+
+static inline void sdma_submit_buffer2(struct sdma *s, void *cookie,
+					void *data1, void *data2, int length)
+{
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(!(s->flags & SDMA_FLAGS_BD2));
+#endif
+	s->cookie[s->index] = cookie;
+	s->bd2[s->index].data1 = data1;
+	s->bd2[s->index].data2 = data2;
+	s->bd2[s->index].status = SDMA_BD_READY | length;
+	s->index = sdma_next_index(s);
+	if (s->flags & SDMA_FLAGS_ENABLE_TASK)
+		sdma_enable_task(s->tasknum);
+}
+
+static inline void *sdma_retrieve_buffer2(struct sdma *s, int *length)
+{
+	void *cookie = s->cookie[s->outdex];
+
+#ifdef CONFIG_BESTCOMM_DEBUG
+	BUG_ON(!(s->flags & SDMA_FLAGS_BD2));
+#endif
+	if (length)
+		*length = s->bd2[s->outdex].status & SDMA_LEN_MASK;
+	s->outdex = sdma_next_outdex(s);
+	return cookie;
+}
+
+#define SDMA_TASK_MAGIC		0x4243544B	/* 'BCTK' */
+
+/* the size fields are given in number of 32-bit words */
+struct sdma_task_header {
+	u32	magic;
+	u8	desc_size;
+	u8	var_size;
+	u8	inc_size;
+	u8	first_var;
+	u8	reserved[8];
+};
+
+#define SDMA_DESC_NOP		0x000001f8
+#define SDMA_LCD_MASK		0x80000000
+#define SDMA_DRD_EXTENDED	0x40000000
+
+#define sdma_drd_is_extended(desc) ((desc) & SDMA_DRD_EXTENDED)
+
+static inline int sdma_desc_is_drd(u32 desc) {
+	return !(desc & SDMA_LCD_MASK) && desc != SDMA_DESC_NOP;
+};
+
+#define SDMA_PRAGMA_BIT_RSV		7	/* reserved pragma bit */
+#define SDMA_PRAGMA_BIT_PRECISE_INC	6	/* increment 0=when possible, */
+						/*	1=iter end */
+#define SDMA_PRAGMA_BIT_RST_ERROR_NO	5	/* don't reset errors on */
+						/* task enable */
+#define SDMA_PRAGMA_BIT_PACK		4	/* pack data enable */
+#define SDMA_PRAGMA_BIT_INTEGER		3	/* data alignment */
+						/* 0=frac(msb), 1=int(lsb) */
+#define SDMA_PRAGMA_BIT_SPECREAD	2	/* XLB speculative read */
+#define SDMA_PRAGMA_BIT_CW		1	/* write line buffer enable */
+#define SDMA_PRAGMA_BIT_RL		0	/* read line buffer enable */
+
+#define SDMA_STD_PRAGMA		((0 << SDMA_PRAGMA_BIT_RSV)		| \
+				 (0 << SDMA_PRAGMA_BIT_PRECISE_INC)	| \
+				 (0 << SDMA_PRAGMA_BIT_RST_ERROR_NO)	| \
+				 (0 << SDMA_PRAGMA_BIT_PACK)		| \
+				 (0 << SDMA_PRAGMA_BIT_INTEGER)		| \
+				 (1 << SDMA_PRAGMA_BIT_SPECREAD)	| \
+				 (1 << SDMA_PRAGMA_BIT_CW)		| \
+				 (1 << SDMA_PRAGMA_BIT_RL))
+
+#define SDMA_PCI_PRAGMA		((0 << SDMA_PRAGMA_BIT_RSV)		| \
+				 (0 << SDMA_PRAGMA_BIT_PRECISE_INC)	| \
+				 (0 << SDMA_PRAGMA_BIT_RST_ERROR_NO)	| \
+				 (0 << SDMA_PRAGMA_BIT_PACK)		| \
+				 (1 << SDMA_PRAGMA_BIT_INTEGER)		| \
+				 (1 << SDMA_PRAGMA_BIT_SPECREAD)	| \
+				 (1 << SDMA_PRAGMA_BIT_CW)		| \
+				 (1 << SDMA_PRAGMA_BIT_RL))
+
+#define SDMA_ATA_PRAGMA		SDMA_STD_PRAGMA
+#define SDMA_CRC16_DP_0_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_CRC16_DP_1_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_FEC_RX_BD_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_FEC_TX_BD_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_0_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_1_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_2_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_3_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_BD_0_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_DP_BD_1_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_RX_BD_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_TX_BD_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_GEN_LPC_PRAGMA	SDMA_STD_PRAGMA
+#define SDMA_PCI_RX_PRAGMA	SDMA_PCI_PRAGMA
+#define SDMA_PCI_TX_PRAGMA	SDMA_PCI_PRAGMA
+
+static inline void sdma_set_task_pragma(int task, int pragma)
+{
+	u32 *fdt = &sdma.tdt[task].fdt;
+	*fdt = (*fdt & ~0xff) | pragma;
+}
+
+static inline void sdma_set_task_auto_start(int task, int next_task)
+{
+	u16 *tcr = &sdma.io->tcr[task];
+	out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task);
+}
+
+#define SDMA_INITIATOR_ALWAYS	 0
+#define SDMA_INITIATOR_SCTMR_0	 1
+#define SDMA_INITIATOR_SCTMR_1	 2
+#define SDMA_INITIATOR_FEC_RX	 3
+#define SDMA_INITIATOR_FEC_TX	 4
+#define SDMA_INITIATOR_ATA_RX	 5
+#define SDMA_INITIATOR_ATA_TX	 6
+#define SDMA_INITIATOR_SCPCI_RX	 7
+#define SDMA_INITIATOR_SCPCI_TX	 8
+#define SDMA_INITIATOR_PSC3_RX	 9
+#define SDMA_INITIATOR_PSC3_TX	10
+#define SDMA_INITIATOR_PSC2_RX	11
+#define SDMA_INITIATOR_PSC2_TX	12
+#define SDMA_INITIATOR_PSC1_RX	13
+#define SDMA_INITIATOR_PSC1_TX	14
+#define SDMA_INITIATOR_SCTMR_2	15
+#define SDMA_INITIATOR_SCLPC	16
+#define SDMA_INITIATOR_PSC5_RX	17
+#define SDMA_INITIATOR_PSC5_TX	18
+#define SDMA_INITIATOR_PSC4_RX	19
+#define SDMA_INITIATOR_PSC4_TX	20
+#define SDMA_INITIATOR_I2C2_RX	21
+#define SDMA_INITIATOR_I2C2_TX	22
+#define SDMA_INITIATOR_I2C1_RX	23
+#define SDMA_INITIATOR_I2C1_TX	24
+#define SDMA_INITIATOR_PSC6_RX	25
+#define SDMA_INITIATOR_PSC6_TX	26
+#define SDMA_INITIATOR_IRDA_RX	25
+#define SDMA_INITIATOR_IRDA_TX	26
+#define SDMA_INITIATOR_SCTMR_3	27
+#define SDMA_INITIATOR_SCTMR_4	28
+#define SDMA_INITIATOR_SCTMR_5	29
+#define SDMA_INITIATOR_SCTMR_6	30
+#define SDMA_INITIATOR_SCTMR_7	31
+
+#define SDMA_IPR_ALWAYS	7
+#define SDMA_IPR_SCTMR_0 	2
+#define SDMA_IPR_SCTMR_1 	2
+#define SDMA_IPR_FEC_RX 	6
+#define SDMA_IPR_FEC_TX 	5
+#define SDMA_IPR_ATA_RX 	4
+#define SDMA_IPR_ATA_TX 	3
+#define SDMA_IPR_SCPCI_RX	2
+#define SDMA_IPR_SCPCI_TX	2
+#define SDMA_IPR_PSC3_RX	2
+#define SDMA_IPR_PSC3_TX	2
+#define SDMA_IPR_PSC2_RX	2
+#define SDMA_IPR_PSC2_TX	2
+#define SDMA_IPR_PSC1_RX	2
+#define SDMA_IPR_PSC1_TX	2
+#define SDMA_IPR_SCTMR_2	2
+#define SDMA_IPR_SCLPC		2
+#define SDMA_IPR_PSC5_RX	2
+#define SDMA_IPR_PSC5_TX	2
+#define SDMA_IPR_PSC4_RX	2
+#define SDMA_IPR_PSC4_TX	2
+#define SDMA_IPR_I2C2_RX	2
+#define SDMA_IPR_I2C2_TX	2
+#define SDMA_IPR_I2C1_RX	2
+#define SDMA_IPR_I2C1_TX	2
+#define SDMA_IPR_PSC6_RX	2
+#define SDMA_IPR_PSC6_TX	2
+#define SDMA_IPR_IRDA_RX	2
+#define SDMA_IPR_IRDA_TX	2
+#define SDMA_IPR_SCTMR_3	2
+#define SDMA_IPR_SCTMR_4	2
+#define SDMA_IPR_SCTMR_5	2
+#define SDMA_IPR_SCTMR_6	2
+#define SDMA_IPR_SCTMR_7	2
+
+extern struct sdma *sdma_alloc(int request_queue_size);
+extern void sdma_free(struct sdma *sdma_struct);
+extern int sdma_load_task(u32 *task_image);
+extern void *sdma_sram_alloc(int size, int alignment, u32 *dma_handle);
+extern void sdma_init_bd(struct sdma *s);
+extern void sdma_init_bd2(struct sdma *s);
+
+#define FIELD_OFFSET(s,f) ((unsigned long)(&(((struct s*)0)->f)))
+
+#endif  /* __BESTCOMM_BESTCOMM_H__ */
diff --git a/arch/ppc/syslib/bestcomm/fec.c b/arch/ppc/syslib/bestcomm/fec.c
new file mode 100644
index 0000000..8756856
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/fec.c
@@ -0,0 +1,174 @@
+/*
+ * arch/ppc/syslib/bestcomm/fec.c
+ *
+ * Driver for MPC52xx processor BestComm FEC controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * HISTORY:
+ *
+ * 2005-08-14	Converted to platform driver by 
+ *		Andrey Volkov <avolkov@varma-el.com>, Varma Electronics Oy
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/errno.h>				  
+#include <asm/io.h>
+
+#include <asm/mpc52xx.h>
+
+#include "bestcomm.h"
+#include "fec.h"
+
+/*
+ * Initialize FEC receive task.
+ * Returns task number of FEC receive task.
+ * Returns -1 on failure
+ */
+int sdma_fec_rx_init(struct sdma *s, phys_addr_t fifo, int maxbufsize)
+{
+	struct sdma_fec_rx_var *var;
+	struct sdma_fec_rx_inc *inc;
+
+	static int tasknum = -1;
+	static struct sdma_bd *bd = 0;
+	static u32 bd_pa;
+
+	if (tasknum < 0) {
+		tasknum = sdma_load_task(sdma_fec_rx_task);
+		if (tasknum < 0)
+			return tasknum;
+	}
+
+	if (!bd) 
+		bd = (struct sdma_bd *)sdma_sram_alloc(sizeof(*bd) * s->num_bd,
+								SDMA_BD_ALIGN, &bd_pa);
+	if (!bd)
+		return -ENOMEM;
+
+	sdma_disable_task(tasknum);
+
+	s->tasknum = tasknum;
+	s->bd = bd;
+	s->flags = SDMA_FLAGS_NONE;
+	s->index = 0;
+	s->outdex = 0;
+	memset(bd, 0, sizeof(*bd) * s->num_bd);
+
+	var = (struct sdma_fec_rx_var *)sdma_task_var(tasknum);
+	var->enable			= sdma_io_pa(&sdma.io->tcr[tasknum]);
+	var->fifo			= fifo;
+	var->bd_base		= bd_pa;
+	var->bd_last		= bd_pa + (s->num_bd - 1)*sizeof(struct sdma_bd);
+	var->bd_start		= bd_pa;
+	var->buffer_size	= maxbufsize;
+
+	/* These are constants, they should have been in the image file */
+	inc = (struct sdma_fec_rx_inc *)sdma_task_inc(tasknum);
+	inc->incr_bytes		= -(s16)sizeof(u32);
+	inc->incr_dst		= sizeof(u32);
+	inc->incr_dst_ma	= sizeof(u8);
+
+	sdma_set_task_pragma(tasknum, SDMA_FEC_RX_BD_PRAGMA);
+	sdma_set_task_auto_start(tasknum, tasknum);
+
+	/* clear pending interrupt bits */
+	out_be32(&sdma.io->IntPend, 1<<tasknum);
+
+	out_8(&sdma.io->ipr[SDMA_INITIATOR_FEC_RX], SDMA_IPR_FEC_RX);
+
+	return tasknum;
+}
+
+/*
+ * Return 2nd to last DRD
+ * This is an ugly hack, but at least it's only done once at initialization
+ */
+static u32 *self_modified_drd(int tasknum)
+{
+	u32 *desc;
+	int num_descs;
+	int drd_count;
+	int i;
+
+	num_descs = sdma_task_num_descs(tasknum);
+	desc = sdma_task_desc(tasknum) + num_descs - 1;
+	drd_count = 0;
+	for (i=0; i<num_descs; i++, desc--)
+		if (sdma_desc_is_drd(*desc) && ++drd_count == 3)
+			break;
+	return desc;
+}
+
+/*
+ * Initialize FEC transmit task.
+ * Returns task number of FEC transmit task.
+ * Returns -1 on failure
+ */
+int sdma_fec_tx_init(struct sdma *s, phys_addr_t fifo)
+{
+	struct sdma_fec_tx_var *var;
+	struct sdma_fec_tx_inc *inc;
+
+	static int tasknum = -1;
+	static struct sdma_bd *bd = 0;
+	static u32 bd_pa;
+
+	if (tasknum < 0) {
+		tasknum = sdma_load_task(sdma_fec_tx_task);
+		if (tasknum < 0)
+			return tasknum;
+	}
+
+	if (!bd)  
+		bd = (struct sdma_bd *)sdma_sram_alloc(sizeof(*bd) * s->num_bd,
+								SDMA_BD_ALIGN, &bd_pa);
+	if (!bd)
+		return -ENOMEM;
+
+	sdma_disable_task(tasknum);
+
+	s->tasknum = tasknum;
+	s->bd = bd;
+	s->flags = SDMA_FLAGS_ENABLE_TASK;
+	s->index = 0;
+	s->outdex = 0;
+	memset(bd, 0, sizeof(*bd) * s->num_bd);
+
+	var = (struct sdma_fec_tx_var *)sdma_task_var(tasknum);
+	var->DRD		= sdma_sram_pa(self_modified_drd(tasknum));
+	var->fifo		= fifo;
+	var->enable		= sdma_io_pa(&sdma.io->tcr[tasknum]);
+	var->bd_base	= bd_pa;
+	var->bd_last	= bd_pa + (s->num_bd - 1)*sizeof(struct sdma_bd);
+	var->bd_start	= bd_pa;
+
+	/* These are constants, they should have been in the image file */
+	inc = (struct sdma_fec_tx_inc *)sdma_task_inc(tasknum);
+	inc->incr_bytes		= -(s16)sizeof(u32);
+	inc->incr_src		= sizeof(u32);
+	inc->incr_src_ma	= sizeof(u8);
+
+	sdma_set_task_pragma(tasknum, SDMA_FEC_TX_BD_PRAGMA);
+	sdma_set_task_auto_start(tasknum, tasknum);
+
+	/* clear pending interrupt bits */
+	out_be32(&sdma.io->IntPend, 1<<tasknum);
+
+	out_8(&sdma.io->ipr[SDMA_INITIATOR_FEC_TX], SDMA_IPR_FEC_TX);
+
+	return tasknum;
+}
+
+EXPORT_SYMBOL(sdma_fec_rx_init);
+EXPORT_SYMBOL(sdma_fec_tx_init);
diff --git a/arch/ppc/syslib/bestcomm/fec.h b/arch/ppc/syslib/bestcomm/fec.h
new file mode 100644
index 0000000..e3abc0f
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/fec.h
@@ -0,0 +1,71 @@
+/*
+ * arch/ppc/syslib/bestcomm/fec.h
+ *
+ * Driver for MPC52xx processor BestComm FEC controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * HISTORY:
+ *
+ * 2005-08-14	Converted to platform driver by 
+ *		Andrey Volkov <avolkov@varma-el.com>, Varma Electronics Oy
+ */
+
+#ifndef __BESTCOMM_FEC_H__
+#define __BESTCOMM_FEC_H__
+
+
+/* rx task vars that need to be set before enabling the task */
+struct sdma_fec_rx_var {
+	u32 enable;		/* (u16*) address of task's control register */
+	u32 fifo;		/* (u32*) address of fec's fifo */
+	u32 bd_base;		/* (struct sdma_bd*) beginning of ring buffer */
+	u32 bd_last;		/* (struct sdma_bd*) end of ring buffer */
+	u32 bd_start;		/* (struct sdma_bd*) current bd */
+	u32 buffer_size;	/* size of receive buffer */
+};
+
+/* rx task incs that need to be set before enabling the task */
+struct sdma_fec_rx_inc {
+	u16 pad0;
+	s16 incr_bytes;
+	u16 pad1;
+	s16 incr_dst;
+	u16 pad2;
+	s16 incr_dst_ma;
+};
+
+/* tx task vars that need to be set before enabling the task */
+struct sdma_fec_tx_var {
+	u32 DRD;		/* (u32*) address of self-modified DRD */
+	u32 fifo;		/* (u32*) address of fec's fifo */
+	u32 enable;		/* (u16*) address of task's control register */
+	u32 bd_base;		/* (struct sdma_bd*) beginning of ring buffer */
+	u32 bd_last;		/* (struct sdma_bd*) end of ring buffer */
+	u32 bd_start;		/* (struct sdma_bd*) current bd */
+	u32 buffer_size;	/* set by uCode for each packet */
+};
+
+/* tx task incs that need to be set before enabling the task */
+struct sdma_fec_tx_inc {
+	u16 pad0;
+	s16 incr_bytes;
+	u16 pad1;
+	s16 incr_src;
+	u16 pad2;
+	s16 incr_src_ma;
+};
+
+extern int sdma_fec_rx_init(struct sdma *s, phys_addr_t fifo, int maxbufsize);
+extern int sdma_fec_tx_init(struct sdma *s, phys_addr_t fifo);
+
+extern u32 sdma_fec_rx_task[];
+extern u32 sdma_fec_tx_task[];
+
+
+#endif  /* __BESTCOMM_FEC_H__ */
diff --git a/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c
new file mode 100644
index 0000000..511b036
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c
@@ -0,0 +1,71 @@
+/*
+ * sdma_fec_rx_task.c
+ *
+ * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 22 11:19:38 2005 GMT
+ */
+
+#include <linux/types.h>
+
+/*
+ * The header consists of the following fields:
+ *	uint32_t	magic;
+ *	uint8_t		desc_size;
+ *	uint8_t		var_size;
+ *	uint8_t		inc_size;
+ *	uint8_t		first_var;
+ *	uint8_t		reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+*/
+
+uint32_t sdma_fec_rx_task[] = {
+	/* header */
+	0x4243544b,
+	0x18060709,
+	0x00000000,
+	0x00000000,
+
+	/* Task descriptors */
+	0x808220e3, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */
+	0x10601010, /*   DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */
+	0xb8800264, /*   LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc4, idx3 += inc4 */
+	0x10001308, /*     DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
+	0x60140002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
+	0x0cccfcca, /*     DRD2B1: *idx3 = EU3(); EU3(*idx3,var10)  */
+	0x80004000, /*   LCDEXT: idx2 = 0x00000000; ; */
+	0xb8c58029, /*   LCD: idx3 = *(idx1 + var00000015); idx3 once var0; idx3 += inc5 */
+	0x60000002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */
+	0x088cf8cc, /*     DRD2B1: idx2 = EU3(); EU3(idx3,var12)  */
+	0x991982f2, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var11; idx2 += inc6, idx3 += inc2 */
+	0x006acf80, /*     DRD1A: *idx3 = *idx0; FN=0 init=3 WS=1 RS=1 */
+	0x80004000, /*   LCDEXT: idx2 = 0x00000000; ; */
+	0x9999802d, /*   LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */
+	0x70000002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
+	0x034cfc4e, /*     DRD2B1: var13 = EU3(); EU3(*idx1,var14)  */
+	0x00008868, /*     DRD1A: idx2 = var13; FN=0 init=0 WS=0 RS=0 */
+	0x99198341, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc0, idx3 += inc1 */
+	0x007ecf80, /*     DRD1A: *idx3 = *idx0; FN=0 init=3 WS=3 RS=3 */
+	0x99198272, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc6, idx3 += inc2 */
+	0x046acf80, /*     DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=1 RS=1 */
+	0x9819002d, /*   LCD: idx2 = idx0; idx2 once var0; idx2 += inc5 */
+	0x0060c790, /*     DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */
+	0x000001f8, /*   NOP */
+
+	/* VAR[9]-VAR[14] */
+	0x40000000,
+	0x7fff7fff,
+	0x00000000,
+	0x00000003,
+	0x40000008,
+	0x43ffffff,
+
+	/* INC[0]-INC[6] */
+	0x40000000,
+	0xe0000000,
+	0xe0000000,
+	0xa0000008,
+	0x20000000,
+	0x00000000,
+	0x4000ffff,
+};
diff --git a/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c
new file mode 100644
index 0000000..d8d7fd3
--- /dev/null
+++ b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c
@@ -0,0 +1,84 @@
+/*
+ * sdma_fec_tx_task.c
+ *
+ * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 22 11:19:29 2005 GMT
+ */
+
+#include <linux/types.h>
+
+/*
+ * The header consists of the following fields:
+ *	uint32_t	magic;
+ *	uint8_t		desc_size;
+ *	uint8_t		var_size;
+ *	uint8_t		inc_size;
+ *	uint8_t		first_var;
+ *	uint8_t		reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+*/
+
+uint32_t sdma_fec_tx_task[] = {
+	/* header */
+	0x4243544b,
+	0x2407070d,
+	0x00000000,
+	0x00000000,
+
+	/* Task descriptors */
+	0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */
+	0x60000005, /*   DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
+	0x01ccfc0d, /*   DRD2B1: var7 = EU3(); EU3(*idx0,var13)  */
+	0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */
+	0x10801418, /*   DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */
+	0xf88103a4, /*   LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var14; idx2 += inc4, idx3 += inc4 */
+	0x801a6024, /*   LCD: idx4 = var0; ; idx4 += inc4 */
+	0x10001708, /*     DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
+	0x60140002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
+	0x0cccfccf, /*     DRD2B1: *idx3 = EU3(); EU3(*idx3,var15)  */
+	0x991a002c, /*   LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */
+	0x70000002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
+	0x024cfc4d, /*     DRD2B1: var9 = EU3(); EU3(*idx1,var13)  */
+	0x60000003, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */
+	0x0cccf247, /*     DRD2B1: *idx3 = EU3(); EU3(var9,var7)  */
+	0x80004000, /*   LCDEXT: idx2 = 0x00000000; ; */
+	0xb8c80029, /*   LCD: idx3 = *(idx1 + var0000001a); idx3 once var0; idx3 += inc5 */
+	0x70000002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
+	0x088cf8d1, /*     DRD2B1: idx2 = EU3(); EU3(idx3,var17)  */
+	0x00002f10, /*     DRD1A: var11 = idx2; FN=0 init=0 WS=0 RS=0 */
+	0x99198432, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var16; idx2 += inc6, idx3 += inc2 */
+	0x008ac398, /*     DRD1A: *idx0 = *idx3; FN=0 init=4 WS=1 RS=1 */
+	0x80004000, /*   LCDEXT: idx2 = 0x00000000; ; */
+	0x9999802d, /*   LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */
+	0x70000002, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
+	0x048cfc53, /*     DRD2B1: var18 = EU3(); EU3(*idx1,var19)  */
+	0x60000008, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=8 EXT init=0 WS=0 RS=0 */
+	0x088cf48b, /*     DRD2B1: idx2 = EU3(); EU3(var18,var11)  */
+	0x99198481, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var18; idx2 += inc0, idx3 += inc1 */
+	0x009ec398, /*     DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */
+	0x991983b2, /*   LCD: idx2 = idx2, idx3 = idx3; idx2 > var14; idx2 += inc6, idx3 += inc2 */
+	0x088ac398, /*     DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */
+	0x9919002d, /*   LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */
+	0x60000005, /*     DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
+	0x0c4cf88e, /*     DRD2B1: *idx1 = EU3(); EU3(idx2,var14)  */
+	0x000001f8, /*   NOP */
+
+	/* VAR[13]-VAR[19] */
+	0x0c000000,
+	0x40000000,
+	0x7fff7fff,
+	0x00000000,
+	0x00000003,
+	0x40000004,
+	0x43ffffff,
+
+	/* INC[0]-INC[6] */
+	0x40000000,
+	0xe0000000,
+	0xe0000000,
+	0xa0000008,
+	0x20000000,
+	0x00000000,
+	0x4000ffff,
+};
diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c
index 7487539..abe6161 100644
--- a/arch/ppc/syslib/mpc52xx_devices.c
+++ b/arch/ppc/syslib/mpc52xx_devices.c
@@ -33,6 +33,28 @@ static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = {
    possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */
 
 struct platform_device ppc_sys_platform_devices[] = {
+	[MPC52xx_SDMA] = {
+		.name		= "mpc52xx-sdma",
+		.id		= -1,
+		.num_resources	= 3,
+		.resource	= (struct resource[]) {
+			{
+				.start	= 0x1200,
+				.end	= 0x12ff,
+				.flags	= IORESOURCE_MEM,
+			},
+			{
+				.start	= 0x8000,
+				.end	= 0xbfff,
+				.flags	= IORESOURCE_MEM,
+			},
+			{
+				.start	= MPC52xx_SDMA_IRQ,
+				.end	= MPC52xx_SDMA_IRQ,
+				.flags	= IORESOURCE_IRQ,
+			},
+		},
+	},
 	[MPC52xx_MSCAN1] = {
 		.name		= "mpc52xx-mscan",
 		.id		= 0,
diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c
index b4e6f97..a74b19c 100644
--- a/arch/ppc/syslib/mpc52xx_sys.c
+++ b/arch/ppc/syslib/mpc52xx_sys.c
@@ -19,10 +19,10 @@ struct ppc_sys_spec ppc_sys_specs[] = {
 		.ppc_sys_name	= "5200",
 		.mask		= 0xffff0000,
 		.value		= 0x80110000,
-		.num_devices	= 15,
+		.num_devices	= MPC52xx_NUM_DEVICES,
 		.device_list	= (enum ppc_sys_devices[])
 		{
-			MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI,
+			MPC52xx_SDMA, MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI,
 			MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2,
 			MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6,
 			MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2,
diff --git a/include/asm-ppc/mpc52xx.h b/include/asm-ppc/mpc52xx.h
index d9d21aa..4856b74 100644
--- a/include/asm-ppc/mpc52xx.h
+++ b/include/asm-ppc/mpc52xx.h
@@ -34,6 +34,8 @@ struct pt_regs;
 /* ======================================================================== */
 
 enum ppc_sys_devices {
+	MPC52xx_SDMA,				 /* Must be first device to probe, 
+									else FEC/ATA will failed */
 	MPC52xx_MSCAN1,
 	MPC52xx_MSCAN2,
 	MPC52xx_SPI,
-- 
1.4.4.2