* [PATCH 0/9] BestComm : better late than never heh ;)
@ 2007-05-12 20:31 Sylvain Munaut
2007-05-12 20:31 ` [PATCH 1/9] powerpc: exports rheap symbol to modules Sylvain Munaut
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML
Hi everyone,
Here we go : This series adds support for the BestComm engine
found on the 5200 CPUs. This is _long_ overdue, I admit it ...
but better late than never ;)
For the little story, Freescale does provide an API that is
just ugly. A long time ago, Dale Farnsworth proposed a new API.
It was a lot better but some things were still not right, some
time after that Andrey Volkov & myself did some rework but still
not enough and with the switch to arch/powerpc there was new stuff
to be done ... Finally, I decided to give it a last go and here
we are ...
A few words about theses patches : The first 3 are just preliminary
stuff, then comes the core, followed by 3 tasks and finally two
drivers. The drivers are not ready for upstream, they are just
here so that people can test that it _does_ work.
About the API itself :
The API divides the work in three layers, trying to keep them as
separate as possible :
1) Core : This is the grunt work, setting up tasks and global
config. It also provides useful helpers for the other
layers and debug help.
This is mainly bestcomm.c bestcomm.h bestcomm_priv.h sram.{c,h}
2) Task support : Each task may have a different way to configure
it's buffer ring or other details. We hide the details of it
in theses. The code in this "layer" has knowelge of bestcomm
internals and can include "bestcomm_priv.h". The microcode
files are mostly automatically extracted from the dma_image.hex
image provided by freescale.
For the ata task: ata.h ata.c bcom_ata_task.c
For the fec task: fec.h fec.c bcom_fec_rx_task.c bcom_fec_tx_task.c
For the genbd task: gen_bd.h gen_bd.c bcom_gen_bd_rx_task.c bcom_gen_bd_tx_task.c
3) Driver: Inside the driver, the knowelge of bestcomm internals
is minimal. You just need to call the task support code to get
your 'dma object' and then if the task is buffer descriptor based,
you cal use the helper function to submit / retrieve bd.
I personnaly like this division because that hides microcode details
from the driver and keeps it separate. It also keeps a lot of code
common, independant of the _precise_ buffer descriptor format. They
all have a status word followed by some address (the exact number depends
of the task ...)
About some details of 'form':
- 'bcom_' sounded like a good prefix. sdma_ was used else where for
another dma engine.
- arch/powerpc/sysdev/bestcomm sounded like a good place to put this.
Final note :
Is the API perfect ? probably not, but I think it's pretty good.
And I hope you'll like it because I don't really feel like starting
from scratch again ... ;)
The two things that I know I need to find a solution to are :
- Initiator priority shouldn't be fixed. The user should be able
to provide it's own in the DT. (if none is found, just fall back
to the default one)
- The link from device -> initiator number is ... not easy to make.
Currently it's fixed (only one FEC so if someone wants the fec
task that must be the FEC driver ...)
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 1/9] powerpc: exports rheap symbol to modules
2007-05-12 20:31 [PATCH 0/9] BestComm : better late than never heh ;) Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Sylvain Munaut
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
Theses can be useful in modules too. So we export them.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/lib/rheap.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c
index b2f6dcc..7c5968c 100644
--- a/arch/powerpc/lib/rheap.c
+++ b/arch/powerpc/lib/rheap.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
@@ -724,3 +725,19 @@ void rh_dump_blk(rh_info_t * info, rh_block_t * blk)
"blk @0x%p: 0x%lx-0x%lx (%u)\n",
blk, blk->start, blk->start + blk->size, blk->size);
}
+
+
+EXPORT_SYMBOL(rh_create);
+EXPORT_SYMBOL(rh_destroy);
+EXPORT_SYMBOL(rh_init);
+EXPORT_SYMBOL(rh_attach_region);
+EXPORT_SYMBOL(rh_detach_region);
+EXPORT_SYMBOL(rh_alloc_align);
+EXPORT_SYMBOL(rh_alloc);
+EXPORT_SYMBOL(rh_alloc_fixed);
+EXPORT_SYMBOL(rh_free);
+EXPORT_SYMBOL(rh_get_stats);
+EXPORT_SYMBOL(rh_set_owner);
+EXPORT_SYMBOL(rh_dump);
+EXPORT_SYMBOL(rh_dump_blk);
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 2/9] powerpc: Changes the config mechanism for rheap
2007-05-12 20:31 ` [PATCH 1/9] powerpc: exports rheap symbol to modules Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Sylvain Munaut
2007-05-15 21:20 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Kumar Gala
0 siblings, 2 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
Instead of having in the makefile all the option that
requires rheap, we define a configuration symbol
and when needed we make sure it's selected.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/Kconfig | 2 ++
arch/powerpc/lib/Kconfig | 3 +++
arch/powerpc/lib/Makefile | 4 +---
arch/powerpc/platforms/Kconfig | 2 ++
4 files changed, 8 insertions(+), 3 deletions(-)
create mode 100644 arch/powerpc/lib/Kconfig
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 56d3c0d..094e5d2 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -193,6 +193,7 @@ config PPC_8xx
bool "Freescale 8xx"
select FSL_SOC
select 8xx
+ select PPC_LIB_RHEAP
config 40x
bool "AMCC 40x"
@@ -885,6 +886,7 @@ source "fs/Kconfig"
source "arch/powerpc/sysdev/qe_lib/Kconfig"
+source "arch/powerpc/lib/Kconfig"
source "lib/Kconfig"
menu "Instrumentation Support"
diff --git a/arch/powerpc/lib/Kconfig b/arch/powerpc/lib/Kconfig
new file mode 100644
index 0000000..f383ad4
--- /dev/null
+++ b/arch/powerpc/lib/Kconfig
@@ -0,0 +1,3 @@
+config PPC_LIB_RHEAP
+ bool
+ default n
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 0a486d4..a6cf399 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -13,7 +13,6 @@ endif
obj-$(CONFIG_PPC64) += checksum_64.o copypage_64.o copyuser_64.o \
memcpy_64.o usercopy_64.o mem_64.o string.o
-obj-$(CONFIG_QUICC_ENGINE) += rheap.o
obj-$(CONFIG_XMON) += sstep.o
obj-$(CONFIG_KPROBES) += sstep.o
obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
@@ -23,5 +22,4 @@ obj-$(CONFIG_SMP) += locks.o
endif
# Temporary hack until we have migrated to asm-powerpc
-obj-$(CONFIG_8xx) += rheap.o
-obj-$(CONFIG_CPM2) += rheap.o
+obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 361acfa..8432f56 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -242,6 +242,7 @@ config TAU_AVERAGE
config QUICC_ENGINE
bool
+ select PPC_LIB_RHEAP
help
The QUICC Engine (QE) is a new generation of communications
coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
@@ -251,6 +252,7 @@ config QUICC_ENGINE
config CPM2
bool
default n
+ select PPC_LIB_RHEAP
help
The CPM2 (Communications Processor Module) is a coprocessor on
embedded CPUs made by Freescale. Selecting this option means that
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes
2007-05-12 20:31 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
2007-05-13 23:46 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Matt Sealey
2007-05-15 21:20 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Kumar Gala
1 sibling, 2 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
On the mpc5200b the ccr register is 32 bits wide while on the
mpc5200 it's only 16 bits. It's up to the driver to use the
correct format depending on the chip it's running on.
The 5200b also offers some more registers & status in AC97
mode. Again, if not running on a 5200b the driver should not
use those.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
include/asm-ppc/mpc52xx_psc.h | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/asm-ppc/mpc52xx_psc.h b/include/asm-ppc/mpc52xx_psc.h
index 9d850b2..c82b8d4 100644
--- a/include/asm-ppc/mpc52xx_psc.h
+++ b/include/asm-ppc/mpc52xx_psc.h
@@ -28,6 +28,10 @@
#define MPC52xx_PSC_MAXNUM 6
/* Programmable Serial Controller (PSC) status register bits */
+#define MPC52xx_PSC_SR_UNEX_RX 0x0001
+#define MPC52xx_PSC_SR_DATA_VAL 0x0002
+#define MPC52xx_PSC_SR_DATA_OVR 0x0004
+#define MPC52xx_PSC_SR_CMDSEND 0x0008
#define MPC52xx_PSC_SR_CDE 0x0080
#define MPC52xx_PSC_SR_RXRDY 0x0100
#define MPC52xx_PSC_SR_RXFULL 0x0200
@@ -132,8 +136,10 @@ struct mpc52xx_psc {
u8 reserved5[3];
u8 ctlr; /* PSC + 0x1c */
u8 reserved6[3];
- u16 ccr; /* PSC + 0x20 */
- u8 reserved7[14];
+ u32 ccr; /* PSC + 0x20 */
+ u32 ac97_slots; /* PSC + 0x24 */
+ u32 ac97_cmd; /* PSC + 0x28 */
+ u32 ac97_data; /* PSC + 0x2c */
u8 ivr; /* PSC + 0x30 */
u8 reserved8[3];
u8 ip; /* PSC + 0x34 */
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 20:31 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
` (2 more replies)
2007-05-13 23:46 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Matt Sealey
1 sibling, 3 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
This patch adds support for the core of the BestComm API
for the Freescale MPC5200(b). The BestComm engine is a
microcode-controlled / tasks-based DMA used by several
of the onchip devices.
Setting up the tasks / memory allocation and all common
low level functions are handled by this patch.
The specifics details of each tasks and their microcode
are split-out in separate patches.
This is not the official API, but a much cleaner one.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/platforms/Kconfig | 2 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/bestcomm/Kconfig | 18 +
arch/powerpc/sysdev/bestcomm/Makefile | 8 +
arch/powerpc/sysdev/bestcomm/bestcomm.c | 600 ++++++++++++++++++++++++++
arch/powerpc/sysdev/bestcomm/bestcomm.h | 136 ++++++
arch/powerpc/sysdev/bestcomm/bestcomm_priv.h | 325 ++++++++++++++
arch/powerpc/sysdev/bestcomm/sram.c | 180 ++++++++
arch/powerpc/sysdev/bestcomm/sram.h | 54 +++
9 files changed, 1324 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/bestcomm/Kconfig
create mode 100644 arch/powerpc/sysdev/bestcomm/Makefile
create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.c
create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.h
create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
create mode 100644 arch/powerpc/sysdev/bestcomm/sram.c
create mode 100644 arch/powerpc/sysdev/bestcomm/sram.h
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 8432f56..fc170a3 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -259,4 +259,6 @@ config CPM2
you wish to build a kernel for a machine with a CPM2 coprocessor
on it (826x, 827x, 8560).
+source "arch/powerpc/sysdev/bestcomm/Kconfig"
+
endmenu
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index c3ce0bd..89074f8 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o
obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o
obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
+obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
mv64x60-$(CONFIG_PCI) += mv64x60_pci.o
obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o
diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig
new file mode 100644
index 0000000..3366e24
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/Kconfig
@@ -0,0 +1,18 @@
+#
+# Kconfig options for Bestcomm
+#
+
+config PPC_BESTCOMM
+ tristate "Bestcomm DMA engine support"
+ depends on PPC_MPC52xx
+ default n
+ select PPC_LIB_RHEAP
+ help
+ BestComm is the name of the communication coprocessor found
+ on the Freescale MPC5200 family of processor. It's usage is
+ optionnal for some drivers (like ATA), but required for
+ others (like FEC).
+
+ If you want to use drivers that require DMA operations,
+ answer Y or M. Otherwise say N.
+
diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile
new file mode 100644
index 0000000..a24aa06
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for BestComm & co
+#
+
+bestcomm-core-objs := bestcomm.o sram.o
+
+obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
+
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c
new file mode 100644
index 0000000..0063a1e
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c
@@ -0,0 +1,600 @@
+/*
+ * Driver for MPC52xx processor BestComm peripheral controller
+ *
+ *
+ * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2005 Varma Electronics Oy,
+ * ( by Andrey Volkov <avolkov@varma-el.com> )
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/mpc52xx.h>
+
+#include "sram.h"
+#include "bestcomm_priv.h"
+#include "bestcomm.h"
+
+#define DRIVER_NAME "bestcomm-core"
+
+
+struct bcom_engine *bcom = NULL;
+
+
+/* ======================================================================== */
+/* Public and private API */
+/* ======================================================================== */
+
+/* Debug Dump */
+
+#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a, ## b)
+
+void
+bcom_dump_status(void)
+{
+ int i;
+ struct mpc52xx_sdma __iomem *r = bcom->regs;
+
+ BCOM_DPRINTK("BestComm status dump (pa=%08lx, va=%p)\n",
+ bcom->regs_base, bcom->regs);
+ BCOM_DPRINTK(" taskBar = %08x\n", in_be32(&r->taskBar));
+ BCOM_DPRINTK(" currentPointer = %08x\n", in_be32(&r->currentPointer));
+ BCOM_DPRINTK(" endPointer = %08x\n", in_be32(&r->endPointer));
+ BCOM_DPRINTK(" variablePointer = %08x\n", in_be32(&r->variablePointer));
+ BCOM_DPRINTK(" IntVect1 = %08x\n", (u32)in_8(&r->IntVect1));
+ BCOM_DPRINTK(" IntVect2 = %08x\n", (u32)in_8(&r->IntVect2));
+ BCOM_DPRINTK(" PtdCntrl = %08hx\n", in_be16(&r->PtdCntrl));
+ BCOM_DPRINTK(" IntPend = %08x\n", in_be32(&r->IntPend));
+ BCOM_DPRINTK(" IntMask = %08x\n", in_be32(&r->IntMask));
+
+ BCOM_DPRINTK(" TCR dump :\n");
+
+ for (i=0; i<16; i++) {
+ printk("%s%04hx%s",
+ (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "",
+ in_be16(&r->tcr[i]),
+ (i&0x7) == 0x7 ? "\n" : " ");
+ }
+
+ BCOM_DPRINTK(" IPR dump :\n");
+
+ for (i=0; i<32; i++) {
+ printk("%s%02x%s",
+ (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "",
+ (u32)in_8(&r->ipr[i]),
+ (i&0x7) == 0x7 ? "\n" : " ");
+ }
+
+ BCOM_DPRINTK(" cReqSelect = %08x\n", in_be32(&r->cReqSelect));
+ BCOM_DPRINTK(" task_size0 = %08x\n", in_be32(&r->task_size0));
+ BCOM_DPRINTK(" task_size1 = %08x\n", in_be32(&r->task_size1));
+ BCOM_DPRINTK(" MDEDebug = %08x\n", in_be32(&r->MDEDebug));
+ BCOM_DPRINTK(" ADSDebug = %08x\n", in_be32(&r->ADSDebug));
+ BCOM_DPRINTK(" Value1 = %08x\n", in_be32(&r->Value1));
+ BCOM_DPRINTK(" Value2 = %08x\n", in_be32(&r->Value2));
+ BCOM_DPRINTK(" Control = %08x\n", in_be32(&r->Control));
+ BCOM_DPRINTK(" Status = %08x\n", in_be32(&r->Status));
+ BCOM_DPRINTK(" PTDDebug = %08x\n", in_be32(&r->PTDDebug));
+}
+
+void
+bcom_dump_task(int task)
+{
+ int i;
+ u32 *p;
+ struct bcom_tdt *tdt = &bcom->tdt[task];
+
+ BCOM_DPRINTK("Task dump %d\n", task);
+ BCOM_DPRINTK(" tcr = %04hx\n", bcom->regs->tcr[task]);
+ BCOM_DPRINTK(" tdt = %p\n", &bcom->tdt[task]);
+ BCOM_DPRINTK(" tdt->start = %08x\n", tdt->start);
+ BCOM_DPRINTK(" tdt->stop = %08x\n", tdt->stop);
+ BCOM_DPRINTK(" tdt->var = %08x\n", tdt->var);
+ BCOM_DPRINTK(" tdt->fdt = %08x\n", tdt->fdt);
+ BCOM_DPRINTK(" tdt->status = %08x\n", tdt->exec_status);
+ BCOM_DPRINTK(" tdt->mvtp = %08x\n", tdt->mvtp);
+ BCOM_DPRINTK(" tdt->context = %08x\n", tdt->context);
+ BCOM_DPRINTK(" tdt->litbase = %08x\n", tdt->litbase);
+
+ BCOM_DPRINTK(" code :\n");
+
+ p = bcom_task_desc(task);
+ for (i=0; i<bcom_task_num_descs(task); i++)
+ printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]);
+
+ BCOM_DPRINTK(" var/inc :\n");
+
+ p = bcom_task_var(task);
+ for (i=0; i<BCOM_MAX_VAR+BCOM_MAX_INC; i++)
+ printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]);
+}
+
+void
+bcom_dump_bdring(struct bcom_task *tsk)
+{
+ int i, j;
+
+ BCOM_DPRINTK("BD ring dump %d\n", tsk->tasknum);
+
+ for (i=0; i<tsk->num_bd; i++) {
+ BCOM_DPRINTK(" BD[%02d] :\n", i);
+ BCOM_DPRINTK(" cookie : %p\n", tsk->cookie[i]);
+ BCOM_DPRINTK(" status : %08x\n", tsk->bd[i].status);
+ for (j=0; j<(tsk->bd_size/sizeof(u32))-1; j++)
+ BCOM_DPRINTK(" data[%02d] : %08x\n",
+ j, tsk->bd[i].data[j]);
+ }
+}
+
+
+/* Private API */
+
+struct bcom_task *
+bcom_task_alloc(int bd_count, int bd_size, int priv_size)
+{
+ int i, tasknum = -1;
+ struct bcom_task *tsk;
+
+ /* Get and reserve a task num */
+ spin_lock(&bcom->lock);
+
+ for (i=0; i<BCOM_MAX_TASKS; i++)
+ if (!bcom->tdt[i].stop) { /* we use stop as a marker */
+ bcom->tdt[i].stop = 0xfffffffful; /* dummy addr */
+ tasknum = i;
+ break;
+ }
+
+ spin_unlock(&bcom->lock);
+
+ if (tasknum < 0)
+ return NULL;
+
+ /* Allocate our structure */
+ tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);
+ if (!tsk)
+ goto error;
+
+ tsk->tasknum = tasknum;
+ if (priv_size)
+ tsk->priv = (void*)tsk + sizeof(struct bcom_task);
+
+ /* Get IRQ of that task */
+ tsk->irq = irq_of_parse_and_map(bcom->ofnode, tsk->tasknum);
+ if (tsk->irq == NO_IRQ)
+ goto error;
+
+ /* Init the BDs, if needed */
+ if (bd_count) {
+ tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL);
+ if (!tsk->cookie)
+ goto error;
+
+ tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);
+ if (!tsk->bd)
+ goto error;
+ memset(tsk->bd, 0x00, bd_count * bd_size);
+
+ tsk->num_bd = bd_count;
+ tsk->bd_size = bd_size;
+ }
+
+ return tsk;
+
+error:
+ if (tsk) {
+ if (tsk->irq != NO_IRQ)
+ irq_dispose_mapping(tsk->irq);
+ bcom_sram_free(tsk->bd);
+ kfree(tsk->cookie);
+ kfree(tsk);
+ }
+
+ bcom->tdt[tasknum].stop = 0;
+
+ return NULL;
+}
+
+void
+bcom_task_release(struct bcom_task *tsk)
+{
+ /* Stop the task */
+ bcom_disable_task(tsk->tasknum);
+
+ /* Clear TDT */
+ bcom->tdt[tsk->tasknum].start = 0;
+ bcom->tdt[tsk->tasknum].stop = 0;
+
+ /* Free everything */
+ irq_dispose_mapping(tsk->irq);
+ bcom_sram_free(tsk->bd);
+ kfree(tsk->cookie);
+ kfree(tsk);
+}
+
+int
+bcom_load_image(int task, u32 *task_image)
+{
+ struct bcom_task_header *hdr = (struct bcom_task_header *)task_image;
+ struct bcom_tdt *tdt;
+ u32 *desc, *var, *inc;
+ u32 *desc_src, *var_src, *inc_src;
+
+ /* Safety checks */
+ if (hdr->magic != BCOM_TASK_MAGIC) {
+ printk(KERN_ERR DRIVER_NAME
+ ": Trying to load invalid microcode\n");
+ return -EINVAL;
+ }
+
+ if ((task < 0) || (task >= BCOM_MAX_TASKS)) {
+ printk(KERN_ERR DRIVER_NAME
+ ": Trying to load invalid task %d\n", task);
+ return -EINVAL;
+ }
+
+ /* Initial load or reload */
+ tdt = &bcom->tdt[task];
+
+ if (tdt->start) {
+ desc = bcom_task_desc(task);
+ if (hdr->desc_size != bcom_task_num_descs(task)) {
+ printk(KERN_ERR DRIVER_NAME
+ ": Trying to reload wrong task image "
+ "(%d size %d/%d)!\n",
+ task,
+ hdr->desc_size,
+ bcom_task_num_descs(task));
+ return -EINVAL;
+ }
+ } else {
+ phys_addr_t start_pa;
+
+ desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);
+ if (!desc)
+ return -ENOMEM;
+
+ tdt->start = start_pa;
+ tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));
+ }
+
+ var = bcom_task_var(task);
+ inc = bcom_task_inc(task);
+
+ /* Clear & copy */
+ memset(var, 0x00, BCOM_VAR_SIZE);
+ memset(inc, 0x00, BCOM_INC_SIZE);
+
+ desc_src = (u32 *)(hdr + 1);
+ var_src = desc_src + hdr->desc_size;
+ inc_src = var_src + hdr->var_size;
+
+ memcpy(desc, desc_src, hdr->desc_size * sizeof(u32));
+ memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));
+ memcpy(inc, inc_src, hdr->inc_size * sizeof(u32));
+
+ return 0;
+}
+
+void
+bcom_set_initiator(int task, int initiator)
+{
+ int i;
+ int num_descs;
+ u32 *desc;
+ int next_drd_has_initiator;
+
+ bcom_set_tcr_initiator(task, initiator);
+
+ /* Just setting tcr is apparently not enough due to some problem */
+ /* with it. So we just go thru all the microcode and replace in */
+ /* the DRD directly */
+
+ desc = bcom_task_desc(task);
+ next_drd_has_initiator = 1;
+ num_descs = bcom_task_num_descs(task);
+
+ for (i=0; i<num_descs; i++, desc++) {
+ if (!bcom_desc_is_drd(*desc))
+ continue;
+ if (next_drd_has_initiator)
+ if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)
+ bcom_set_desc_initiator(desc, initiator);
+ next_drd_has_initiator = !bcom_drd_is_extended(*desc);
+ }
+}
+
+
+/* Public API */
+
+void
+bcom_enable(struct bcom_task *tsk)
+{
+ bcom_enable_task(tsk->tasknum);
+}
+
+void
+bcom_disable(struct bcom_task *tsk)
+{
+ bcom_disable_task(tsk->tasknum);
+}
+
+
+/* ======================================================================== */
+/* Engine init/cleanup */
+/* ======================================================================== */
+
+/* Function Descriptor table */
+/* this will need to be updated if Freescale changes their task code FDT */
+static u32 fdt_ops[] = {
+ 0xa0045670, /* FDT[48] - load_acc() */
+ 0x80045670, /* FDT[49] - unload_acc() */
+ 0x21800000, /* FDT[50] - and() */
+ 0x21e00000, /* FDT[51] - or() */
+ 0x21500000, /* FDT[52] - xor() */
+ 0x21400000, /* FDT[53] - andn() */
+ 0x21500000, /* FDT[54] - not() */
+ 0x20400000, /* FDT[55] - add() */
+ 0x20500000, /* FDT[56] - sub() */
+ 0x20800000, /* FDT[57] - lsh() */
+ 0x20a00000, /* FDT[58] - rsh() */
+ 0xc0170000, /* FDT[59] - crc8() */
+ 0xc0145670, /* FDT[60] - crc16() */
+ 0xc0345670, /* FDT[61] - crc32() */
+ 0xa0076540, /* FDT[62] - endian32() */
+ 0xa0000760, /* FDT[63] - endian16() */
+};
+
+
+static int __devinit
+bcom_engine_init(void)
+{
+ int task;
+ phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
+ unsigned int tdt_size, ctx_size, var_size, fdt_size;
+
+ /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
+ tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
+ ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
+ var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
+ fdt_size = BCOM_FDT_SIZE;
+
+ bcom->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
+ bcom->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
+ bcom->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
+ bcom->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
+
+ if (!bcom->tdt || !bcom->ctx || !bcom->var || !bcom->fdt) {
+ printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
+
+ bcom_sram_free(bcom->tdt);
+ bcom_sram_free(bcom->ctx);
+ bcom_sram_free(bcom->var);
+ bcom_sram_free(bcom->fdt);
+
+ return -ENOMEM;
+ }
+
+ memset(bcom->tdt, 0x00, tdt_size);
+ memset(bcom->ctx, 0x00, ctx_size);
+ memset(bcom->var, 0x00, var_size);
+ memset(bcom->fdt, 0x00, fdt_size);
+
+ /* Copy the FDT for the EU#3 */
+ memcpy(&bcom->fdt[48], fdt_ops, sizeof(fdt_ops));
+
+ /* Initialize Task base structure */
+ for (task=0; task<BCOM_MAX_TASKS; task++)
+ {
+ out_be16(&bcom->regs->tcr[task], 0);
+ out_8(&bcom->regs->ipr[task], 0);
+
+ bcom->tdt[task].context = ctx_pa;
+ bcom->tdt[task].var = var_pa;
+ bcom->tdt[task].fdt = fdt_pa;
+
+ var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
+ ctx_pa += BCOM_CTX_SIZE;
+ }
+
+ out_be32(&bcom->regs->taskBar, tdt_pa);
+
+ /* Init 'always' initiator */
+ out_8(&bcom->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
+
+ /* Disable COMM Bus Prefetch, apparently it's not reliable yet */
+ /* FIXME: This should be done on 5200 and not 5200B ... */
+ out_be16(&bcom->regs->PtdCntrl, in_be16(&bcom->regs->PtdCntrl) | 1);
+
+ /* Init lock */
+ spin_lock_init(&bcom->lock);
+
+ return 0;
+}
+
+static void
+bcom_engine_cleanup(void)
+{
+ int task;
+
+ /* Stop all tasks */
+ for (task=0; task<BCOM_MAX_TASKS; task++)
+ {
+ out_be16(&bcom->regs->tcr[task], 0);
+ out_8(&bcom->regs->ipr[task], 0);
+ }
+
+ out_be32(&bcom->regs->taskBar, 0ul);
+
+ /* Release the SRAM zones */
+ bcom_sram_free(bcom->tdt);
+ bcom_sram_free(bcom->ctx);
+ bcom_sram_free(bcom->var);
+ bcom_sram_free(bcom->fdt);
+}
+
+
+/* ======================================================================== */
+/* System/Module init & cleanup */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_bcom_init(void)
+{
+ struct device_node *ofn_bcom, *ofn_sram;
+ struct resource res_bcom;
+
+ int rv;
+
+ /* Find the bestcomm node. If none, fails 'silently' since
+ * we may just be on another platform */
+ ofn_bcom = of_find_compatible_node(
+ NULL, "dma-controller", "mpc5200-bestcomm");
+ if (!ofn_bcom)
+ return -ENODEV;
+
+ /* Inform user we're ok so far */
+ printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
+
+ /* Prepare SRAM */
+ ofn_sram = of_find_compatible_node(NULL, "sram", "mpc5200-sram");
+ if (!ofn_sram) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "No SRAM found in device tree\n");
+ rv = -ENODEV;
+ goto error_ofput;
+ }
+
+ rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
+
+ of_node_put(ofn_sram);
+
+ if (rv) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Error in SRAM init\n");
+ goto error_ofput;
+ }
+
+ /* Get a clean struct */
+ bcom = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
+ if (!bcom) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Can't allocate state structure\n");
+ rv = -ENOMEM;
+ goto error_sramclean;
+ }
+
+ /* Save the node */
+ bcom->ofnode = ofn_bcom;
+
+ /* Get, reserve & map io */
+ if (of_address_to_resource(bcom->ofnode, 0, &res_bcom)) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Can't get resource\n");
+ rv = -EINVAL;
+ goto error_sramclean;
+ }
+
+ if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma),
+ DRIVER_NAME)) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Can't request registers region\n");
+ rv = -EBUSY;
+ goto error_sramclean;
+ }
+
+ bcom->regs_base = res_bcom.start;
+ bcom->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
+ if (!bcom->regs) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Can't map registers\n");
+ rv = -ENOMEM;
+ goto error_release;
+ }
+
+ /* Now, do the real init */
+ rv = bcom_engine_init();
+ if (rv)
+ goto error_unmap;
+
+ /* Done ! */
+ printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
+ bcom->regs_base);
+
+ return 0;
+
+ /* Error path */
+error_unmap:
+ iounmap(bcom->regs);
+error_release:
+ release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
+error_sramclean:
+ bcom_sram_cleanup();
+error_ofput:
+ of_node_put(bcom->ofnode);
+
+ printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
+
+ return rv;
+}
+
+static void __exit
+mpc52xx_bcom_exit(void)
+{
+ /* Clean up the engine */
+ bcom_engine_cleanup();
+
+ /* Cleanup SRAM */
+ bcom_sram_cleanup();
+
+ /* Release regs */
+ iounmap(bcom->regs);
+ release_mem_region(bcom->regs_base, sizeof(struct mpc52xx_sdma));
+
+ /* Release the node */
+ of_node_put(bcom->ofnode);
+
+ /* Release memory */
+ kfree(bcom);
+}
+
+#ifdef MODULE
+module_init(mpc52xx_bcom_init);
+module_exit(mpc52xx_bcom_exit);
+#endif
+
+/* If we're not a module, we must make sure everything is setup before anyone */
+/* tries to use us ... */
+#ifndef MODULE
+subsys_initcall(mpc52xx_bcom_init);
+#endif
+
+MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
+MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
+MODULE_LICENSE("GPL v2");
+
+
+EXPORT_SYMBOL(bcom);
+EXPORT_SYMBOL(bcom_dump_status);
+EXPORT_SYMBOL(bcom_dump_task);
+EXPORT_SYMBOL(bcom_dump_bdring);
+EXPORT_SYMBOL(bcom_task_alloc);
+EXPORT_SYMBOL(bcom_task_release);
+EXPORT_SYMBOL(bcom_load_image);
+EXPORT_SYMBOL(bcom_set_initiator);
+EXPORT_SYMBOL(bcom_enable);
+EXPORT_SYMBOL(bcom_disable);
+
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h
new file mode 100644
index 0000000..eac3eec
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h
@@ -0,0 +1,136 @@
+/*
+ * Public header for the MPC52xx processor BestComm driver
+ *
+ *
+ * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2005 Varma Electronics Oy,
+ * ( by Andrey Volkov <avolkov@varma-el.com> )
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
+ *
+ * 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.
+ */
+
+#ifndef __BESTCOMM_H__
+#define __BESTCOMM_H__
+
+struct bcom_bd; /* defined later on ... */
+
+
+/* ======================================================================== */
+/* Generic task managment */
+/* ======================================================================== */
+
+struct bcom_task {
+ unsigned int tasknum;
+ unsigned int flags;
+ int irq;
+
+ struct bcom_bd *bd;
+ phys_addr_t bd_pa;
+ void **cookie;
+ unsigned short index;
+ unsigned short outdex;
+ unsigned int num_bd;
+ unsigned int bd_size;
+
+ void* priv;
+};
+
+#define BCOM_FLAGS_NONE 0x00000000ul
+#define BCOM_FLAGS_ENABLE_TASK (1ul << 0)
+
+
+extern void bcom_enable(struct bcom_task *tsk);
+extern void bcom_disable(struct bcom_task *tsk);
+
+static inline int
+bcom_get_task_irq(struct bcom_task *tsk) {
+ return tsk->irq;
+}
+
+
+/* Debug dumps */
+extern void bcom_dump_status(void);
+extern void bcom_dump_task(int task);
+extern void bcom_dump_bdring(struct bcom_task *tsk);
+
+
+/* ======================================================================== */
+/* BD based tasks helpers */
+/* ======================================================================== */
+
+struct bcom_bd {
+ u32 status;
+ u32 data[1]; /* variable, but at least 1 */
+};
+
+#define BCOM_BD_READY 0x40000000ul
+
+static inline int /* user shouldn't use this ! */
+_bcom_next_index(struct bcom_task *tsk)
+{
+ return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1;
+}
+
+static inline int /* user shouldn't use this ! */
+_bcom_next_outdex(struct bcom_task *tsk)
+{
+ return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1;
+}
+
+static inline int
+bcom_queue_empty(struct bcom_task *tsk)
+{
+ return tsk->index == tsk->outdex;
+}
+
+static inline int
+bcom_queue_full(struct bcom_task *tsk)
+{
+ return tsk->outdex == _bcom_next_index(tsk);
+}
+
+static inline int
+bcom_buffer_done(struct bcom_task *tsk)
+{
+ if (bcom_queue_empty(tsk))
+ return 0;
+ return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+}
+
+static inline struct bcom_bd *
+bcom_prepare_next_buffer(struct bcom_task *tsk)
+{
+ tsk->bd[tsk->index].status = 0; /* cleanup last status */
+ return &tsk->bd[tsk->index];
+}
+
+static inline void
+bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
+{
+ tsk->cookie[tsk->index] = cookie;
+ mb(); /* ensure the bd is really up-to-date */
+ tsk->bd[tsk->index].status |= BCOM_BD_READY;
+ tsk->index = _bcom_next_index(tsk);
+ if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
+ bcom_enable(tsk);
+}
+
+static inline void *
+bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
+{
+ void *cookie = tsk->cookie[tsk->outdex];
+ if (p_status)
+ *p_status = tsk->bd[tsk->outdex].status;
+ if (p_bd)
+ *p_bd = &tsk->bd[tsk->outdex];
+ tsk->outdex = _bcom_next_outdex(tsk);
+ return cookie;
+}
+
+
+#endif /* __BESTCOMM_H__ */
+
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
new file mode 100644
index 0000000..d43b00a
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
@@ -0,0 +1,325 @@
+/*
+ * Private header for the MPC52xx processor BestComm driver
+ *
+ *
+ * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2005 Varma Electronics Oy,
+ * ( by Andrey Volkov <avolkov@varma-el.com> )
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
+ *
+ * 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.
+ */
+
+#ifndef __BESTCOMM_PRIV_H__
+#define __BESTCOMM_PRIV_H__
+
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/mpc52xx.h>
+
+#include "sram.h"
+
+
+/* ======================================================================== */
+/* Engine related stuff */
+/* ======================================================================== */
+
+/* Zones sizes and needed alignments */
+#define BCOM_MAX_TASKS 16
+#define BCOM_MAX_VAR 24
+#define BCOM_MAX_INC 8
+#define BCOM_MAX_FDT 64
+#define BCOM_MAX_CTX 20
+#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32))
+#define BCOM_CTX_ALIGN 0x100
+#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32))
+#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32))
+#define BCOM_VAR_ALIGN 0x80
+#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32))
+#define BCOM_FDT_ALIGN 0x100
+
+/* Task Descriptor Table Entry */
+struct bcom_tdt {
+ u32 start;
+ u32 stop;
+ u32 var;
+ u32 fdt;
+ u32 exec_status; /* used internally by BestComm engine */
+ u32 mvtp; /* used internally by BestComm engine */
+ u32 context;
+ u32 litbase;
+};
+
+/* This holds all info needed globaly to handle the engine */
+struct bcom_engine {
+ struct device_node *ofnode;
+ struct mpc52xx_sdma __iomem *regs;
+ phys_addr_t regs_base;
+
+ struct bcom_tdt *tdt;
+ u32 *ctx;
+ u32 *var;
+ u32 *fdt;
+
+ spinlock_t lock;
+};
+
+extern struct bcom_engine *bcom;
+
+
+/* ======================================================================== */
+/* Tasks related stuff */
+/* ======================================================================== */
+
+/* Tasks image header */
+#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */
+
+struct bcom_task_header {
+ u32 magic;
+ u8 desc_size; /* the size fields */
+ u8 var_size; /* are given in number */
+ u8 inc_size; /* of 32-bits words */
+ u8 first_var;
+ u8 reserved[8];
+};
+
+/* Descriptors stucture & co */
+#define BCOM_DESC_NOP 0x000001f8
+#define BCOM_LCD_MASK 0x80000000
+#define BCOM_DRD_EXTENDED 0x40000000
+#define BCOM_DRD_INITIATOR_SHIFT 21
+
+/* Tasks pragma */
+#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */
+#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */
+ /* 1=iter end */
+#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */
+ /* task enable */
+#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */
+#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */
+ /* 0=frac(msb), 1=int(lsb) */
+#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */
+#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */
+#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */
+
+ /* Looks like XLB speculative read generates XLB errors when a buffer
+ * is at the end of the physical memory. i.e. when accessing the
+ * lasts words, the engine tries to prefetch the next but there is no
+ * next ...
+ */
+#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
+ (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
+ (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
+ (0 << BCOM_PRAGMA_BIT_PACK) | \
+ (0 << BCOM_PRAGMA_BIT_INTEGER) | \
+ (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
+ (1 << BCOM_PRAGMA_BIT_CW) | \
+ (1 << BCOM_PRAGMA_BIT_RL))
+
+#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
+ (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
+ (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
+ (0 << BCOM_PRAGMA_BIT_PACK) | \
+ (1 << BCOM_PRAGMA_BIT_INTEGER) | \
+ (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
+ (1 << BCOM_PRAGMA_BIT_CW) | \
+ (1 << BCOM_PRAGMA_BIT_RL))
+
+#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA
+#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA
+#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA
+
+/* Initiators number */
+#define BCOM_INITIATOR_ALWAYS 0
+#define BCOM_INITIATOR_SCTMR_0 1
+#define BCOM_INITIATOR_SCTMR_1 2
+#define BCOM_INITIATOR_FEC_RX 3
+#define BCOM_INITIATOR_FEC_TX 4
+#define BCOM_INITIATOR_ATA_RX 5
+#define BCOM_INITIATOR_ATA_TX 6
+#define BCOM_INITIATOR_SCPCI_RX 7
+#define BCOM_INITIATOR_SCPCI_TX 8
+#define BCOM_INITIATOR_PSC3_RX 9
+#define BCOM_INITIATOR_PSC3_TX 10
+#define BCOM_INITIATOR_PSC2_RX 11
+#define BCOM_INITIATOR_PSC2_TX 12
+#define BCOM_INITIATOR_PSC1_RX 13
+#define BCOM_INITIATOR_PSC1_TX 14
+#define BCOM_INITIATOR_SCTMR_2 15
+#define BCOM_INITIATOR_SCLPC 16
+#define BCOM_INITIATOR_PSC5_RX 17
+#define BCOM_INITIATOR_PSC5_TX 18
+#define BCOM_INITIATOR_PSC4_RX 19
+#define BCOM_INITIATOR_PSC4_TX 20
+#define BCOM_INITIATOR_I2C2_RX 21
+#define BCOM_INITIATOR_I2C2_TX 22
+#define BCOM_INITIATOR_I2C1_RX 23
+#define BCOM_INITIATOR_I2C1_TX 24
+#define BCOM_INITIATOR_PSC6_RX 25
+#define BCOM_INITIATOR_PSC6_TX 26
+#define BCOM_INITIATOR_IRDA_RX 25
+#define BCOM_INITIATOR_IRDA_TX 26
+#define BCOM_INITIATOR_SCTMR_3 27
+#define BCOM_INITIATOR_SCTMR_4 28
+#define BCOM_INITIATOR_SCTMR_5 29
+#define BCOM_INITIATOR_SCTMR_6 30
+#define BCOM_INITIATOR_SCTMR_7 31
+
+/* Initiators priorities */
+#define BCOM_IPR_ALWAYS 7
+#define BCOM_IPR_SCTMR_0 2
+#define BCOM_IPR_SCTMR_1 2
+#define BCOM_IPR_FEC_RX 6
+#define BCOM_IPR_FEC_TX 5
+#define BCOM_IPR_ATA_RX 4
+#define BCOM_IPR_ATA_TX 3
+#define BCOM_IPR_SCPCI_RX 2
+#define BCOM_IPR_SCPCI_TX 2
+#define BCOM_IPR_PSC3_RX 2
+#define BCOM_IPR_PSC3_TX 2
+#define BCOM_IPR_PSC2_RX 2
+#define BCOM_IPR_PSC2_TX 2
+#define BCOM_IPR_PSC1_RX 2
+#define BCOM_IPR_PSC1_TX 2
+#define BCOM_IPR_SCTMR_2 2
+#define BCOM_IPR_SCLPC 2
+#define BCOM_IPR_PSC5_RX 2
+#define BCOM_IPR_PSC5_TX 2
+#define BCOM_IPR_PSC4_RX 2
+#define BCOM_IPR_PSC4_TX 2
+#define BCOM_IPR_I2C2_RX 2
+#define BCOM_IPR_I2C2_TX 2
+#define BCOM_IPR_I2C1_RX 2
+#define BCOM_IPR_I2C1_TX 2
+#define BCOM_IPR_PSC6_RX 2
+#define BCOM_IPR_PSC6_TX 2
+#define BCOM_IPR_IRDA_RX 2
+#define BCOM_IPR_IRDA_TX 2
+#define BCOM_IPR_SCTMR_3 2
+#define BCOM_IPR_SCTMR_4 2
+#define BCOM_IPR_SCTMR_5 2
+#define BCOM_IPR_SCTMR_6 2
+#define BCOM_IPR_SCTMR_7 2
+
+
+/* ======================================================================== */
+/* API */
+/* ======================================================================== */
+
+extern struct bcom_task *bcom_task_alloc(int bd_count, int bd_size, int priv_size);
+extern void bcom_task_release(struct bcom_task *tsk);
+
+extern int bcom_load_image(int task, u32 *task_image);
+extern void bcom_set_initiator(int task, int initiator);
+
+
+#define TASK_ENABLE 0x8000
+
+static inline void
+bcom_enable_task(int task)
+{
+ u16 reg;
+ reg = in_be16(&bcom->regs->tcr[task]);
+ out_be16(&bcom->regs->tcr[task], reg | TASK_ENABLE);
+}
+
+static inline void
+bcom_disable_task(int task)
+{
+ u16 reg = in_be16(&bcom->regs->tcr[task]);
+ out_be16(&bcom->regs->tcr[task], reg & ~TASK_ENABLE);
+}
+
+
+static inline u32 *
+bcom_task_desc(int task)
+{
+ return bcom_sram_pa2va(bcom->tdt[task].start);
+}
+
+static inline int
+bcom_task_num_descs(int task)
+{
+ return (bcom->tdt[task].stop - bcom->tdt[task].start)/sizeof(u32) + 1;
+}
+
+static inline u32 *
+bcom_task_var(int task)
+{
+ return bcom_sram_pa2va(bcom->tdt[task].var);
+}
+
+static inline u32 *
+bcom_task_inc(int task)
+{
+ return &bcom_task_var(task)[BCOM_MAX_VAR];
+}
+
+
+static inline int
+bcom_drd_is_extended(u32 desc)
+{
+ return (desc) & BCOM_DRD_EXTENDED;
+}
+
+static inline int
+bcom_desc_is_drd(u32 desc)
+{
+ return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP;
+}
+
+static inline int
+bcom_desc_initiator(u32 desc)
+{
+ return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f;
+}
+
+static inline void
+bcom_set_desc_initiator(u32 *desc, int initiator)
+{
+ *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) |
+ ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT);
+}
+
+
+static inline void
+bcom_set_task_pragma(int task, int pragma)
+{
+ u32 *fdt = &bcom->tdt[task].fdt;
+ *fdt = (*fdt & ~0xff) | pragma;
+}
+
+static inline void
+bcom_set_task_auto_start(int task, int next_task)
+{
+ u16 __iomem *tcr = &bcom->regs->tcr[task];
+ out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task);
+}
+
+static inline void
+bcom_set_tcr_initiator(int task, int initiator)
+{
+ u16 __iomem *tcr = &bcom->regs->tcr[task];
+ out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8));
+}
+
+
+#endif /* __BESTCOMM_PRIV_H__ */
+
diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/sysdev/bestcomm/sram.c
new file mode 100644
index 0000000..4f69127
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/sram.c
@@ -0,0 +1,180 @@
+/*
+ * Simple memory allocator for on-board SRAM
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/mmu.h>
+#include <asm/prom.h>
+
+#include "sram.h"
+
+
+/* Struct keeping our 'state' */
+struct bcom_sram *bcom_sram = NULL;
+
+
+/* ======================================================================== */
+/* Public API */
+/* ======================================================================== */
+/* DO NOT USE in interrupts, if needed in irq handler, we should use the
+ _irqsave version of the spin_locks */
+
+int bcom_sram_init(struct device_node *sram_node, char *owner)
+{
+ int rv;
+ const u32 *regaddr_p;
+ u64 regaddr64, size64;
+ unsigned int psize;
+
+ /* Create our state struct */
+ if (bcom_sram) {
+ printk(KERN_ERR "%s: bcom_sram_init: "
+ "Already initialiwed !\n", owner);
+ return -EBUSY;
+ }
+
+ bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL);
+ if (!bcom_sram) {
+ printk(KERN_ERR "%s: bcom_sram_init: "
+ "Couldn't allocate internal state !\n", owner);
+ return -ENOMEM;
+ }
+
+ /* Get address and size of the sram */
+ regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
+ if (!regaddr_p) {
+ printk(KERN_ERR "%s: bcom_sram_init: "
+ "Invalid device node !\n", owner);
+ rv = -EINVAL;
+ goto error_free;
+ }
+
+ regaddr64 = of_translate_address(sram_node, regaddr_p);
+
+ bcom_sram->base_phys = (phys_addr_t) regaddr64;
+ bcom_sram->size = (unsigned int) size64;
+
+ /* Request region */
+ if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) {
+ printk(KERN_ERR "%s: bcom_sram_init: "
+ "Couln't request region !\n", owner);
+ rv = -EBUSY;
+ goto error_free;
+ }
+
+ /* Map SRAM */
+ /* sram is not really __iomem */
+ bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size);
+
+ if (!bcom_sram->base_virt) {
+ printk(KERN_ERR "%s: bcom_sram_init: "
+ "Map error SRAM zone 0x%08lx (0x%0x)!\n",
+ owner, bcom_sram->base_phys, bcom_sram->size );
+ rv = -ENOMEM;
+ goto error_release;
+ }
+
+ /* Create an rheap (defaults to 32 bits word alignment) */
+ bcom_sram->rh = rh_create(4);
+
+ /* Attach the free zones */
+#if 0
+ /* Currently disabled ... for future use only */
+ reg_addr_p = of_get_property(sram_node, "available", &psize);
+#else
+ regaddr_p = NULL;
+ psize = 0;
+#endif
+
+ if (!regaddr_p || !psize) {
+ /* Attach the whole zone */
+ rh_attach_region(bcom_sram->rh, 0, bcom_sram->size);
+ } else {
+ /* Attach each zone independently */
+ while (psize >= 2 * sizeof(u32)) {
+ phys_addr_t zbase = of_translate_address(sram_node, regaddr_p);
+ rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]);
+ regaddr_p += 2;
+ psize -= 2 * sizeof(u32);
+ }
+ }
+
+ /* Init our spinlock */
+ spin_lock_init(&bcom_sram->lock);
+
+ return 0;
+
+error_release:
+ release_mem_region(bcom_sram->base_phys, bcom_sram->size);
+error_free:
+ kfree(bcom_sram);
+ bcom_sram = NULL;
+
+ return rv;
+}
+
+void bcom_sram_cleanup(void)
+{
+ /* Free resources */
+ if (bcom_sram) {
+ rh_destroy(bcom_sram->rh);
+ iounmap((void __iomem *)bcom_sram->base_virt);
+ release_mem_region(bcom_sram->base_phys, bcom_sram->size);
+ kfree(bcom_sram);
+ bcom_sram = NULL;
+ }
+}
+
+void* bcom_sram_alloc(int size, int align, phys_addr_t *phys)
+{
+ unsigned long offset;
+
+ spin_lock(&bcom_sram->lock);
+ offset = rh_alloc_align(bcom_sram->rh, size, align, NULL);
+ spin_unlock(&bcom_sram->lock);
+
+ if (IS_ERR_VALUE(offset))
+ return NULL;
+
+ *phys = bcom_sram->base_phys + offset;
+ return bcom_sram->base_virt + offset;
+}
+
+void bcom_sram_free(void *ptr)
+{
+ unsigned long offset;
+
+ if (!ptr)
+ return;
+
+ offset = ptr - bcom_sram->base_virt;
+
+ spin_lock(&bcom_sram->lock);
+ rh_free(bcom_sram->rh, offset);
+ spin_unlock(&bcom_sram->lock);
+}
+
+
+EXPORT_SYMBOL(bcom_sram);
+
+EXPORT_SYMBOL(bcom_sram_init);
+EXPORT_SYMBOL(bcom_sram_cleanup);
+EXPORT_SYMBOL(bcom_sram_alloc);
+EXPORT_SYMBOL(bcom_sram_free);
+
diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/arch/powerpc/sysdev/bestcomm/sram.h
new file mode 100644
index 0000000..b6d6689
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/sram.h
@@ -0,0 +1,54 @@
+/*
+ * Handling of a sram zone for bestcomm
+ *
+ *
+ * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ */
+
+#ifndef __BESTCOMM_SRAM_H__
+#define __BESTCOMM_SRAM_H__
+
+#include <asm/rheap.h>
+#include <asm/mmu.h>
+#include <linux/spinlock.h>
+
+
+/* Structure used internally */
+ /* The internals are here for the inline functions
+ * sake, certainly not for the user to mess with !
+ */
+struct bcom_sram {
+ phys_addr_t base_phys;
+ void *base_virt;
+ unsigned int size;
+ rh_info_t *rh;
+ spinlock_t lock;
+};
+
+extern struct bcom_sram *bcom_sram;
+
+
+/* Public API */
+extern int bcom_sram_init(struct device_node *sram_node, char *owner);
+extern void bcom_sram_cleanup(void);
+
+extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys);
+extern void bcom_sram_free(void *ptr);
+
+static inline phys_addr_t bcom_sram_va2pa(void *va) {
+ return bcom_sram->base_phys +
+ (unsigned long)(va - bcom_sram->base_virt);
+}
+
+static inline void *bcom_sram_pa2va(phys_addr_t pa) {
+ return bcom_sram->base_virt +
+ (unsigned long)(pa - bcom_sram->base_phys);
+}
+
+
+#endif /* __BESTCOMM_SRAM_H__ */
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 5/9] powerpc: BestcComm ATA task support
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 6/9] powerpc: BestcComm FEC " Sylvain Munaut
2007-05-12 23:30 ` [PATCH 5/9] powerpc: BestcComm ATA task support Arnd Bergmann
2007-05-12 23:27 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Arnd Bergmann
2007-05-15 21:37 ` Kumar Gala
2 siblings, 2 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
This is the microcode for the ATA task and the associated
support code.
The microcode itself comes directly from the offical
API (v2.2)
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/sysdev/bestcomm/Kconfig | 7 +
arch/powerpc/sysdev/bestcomm/Makefile | 2 +
arch/powerpc/sysdev/bestcomm/ata.c | 155 ++++++++++++++++++++++++++
arch/powerpc/sysdev/bestcomm/ata.h | 37 ++++++
arch/powerpc/sysdev/bestcomm/bcom_ata_task.c | 61 ++++++++++
5 files changed, 262 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/bestcomm/ata.c
create mode 100644 arch/powerpc/sysdev/bestcomm/ata.h
create mode 100644 arch/powerpc/sysdev/bestcomm/bcom_ata_task.c
diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig
index 3366e24..9d087ce 100644
--- a/arch/powerpc/sysdev/bestcomm/Kconfig
+++ b/arch/powerpc/sysdev/bestcomm/Kconfig
@@ -16,3 +16,10 @@ config PPC_BESTCOMM
If you want to use drivers that require DMA operations,
answer Y or M. Otherwise say N.
+config PPC_BESTCOMM_ATA
+ tristate "Bestcomm ATA task support"
+ depends on PPC_BESTCOMM
+ default n
+ help
+ This option enables the support for the ATA task.
+
diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile
index a24aa06..b7a6a40 100644
--- a/arch/powerpc/sysdev/bestcomm/Makefile
+++ b/arch/powerpc/sysdev/bestcomm/Makefile
@@ -3,6 +3,8 @@
#
bestcomm-core-objs := bestcomm.o sram.o
+bestcomm-ata-objs := ata.o bcom_ata_task.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
+obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o
diff --git a/arch/powerpc/sysdev/bestcomm/ata.c b/arch/powerpc/sysdev/bestcomm/ata.c
new file mode 100644
index 0000000..c14f727
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/ata.c
@@ -0,0 +1,155 @@
+/*
+ * Bestcomm ATA task driver
+ *
+ *
+ * Patterned after bestcomm/fec.c by Dale Farnsworth <dfarnsworth@mvista.com>
+ * 2003-2004 (c) MontaVista, Software, Inc.
+ *
+ * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2006 Freescale - John Rigby
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "bestcomm.h"
+#include "bestcomm_priv.h"
+#include "ata.h"
+
+
+/* ======================================================================== */
+/* Task image/var/inc */
+/* ======================================================================== */
+
+/* ata task image */
+extern u32 bcom_ata_task[];
+
+/* ata task vars that need to be set before enabling the task */
+struct bcom_ata_var {
+ u32 enable; /* (u16*) address of task's control register */
+ u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
+ u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
+ u32 bd_start; /* (struct bcom_bd*) current bd */
+ u32 buffer_size; /* size of receive buffer */
+};
+
+/* ata task incs that need to be set before enabling the task */
+struct bcom_ata_inc {
+ u16 pad0;
+ s16 incr_bytes;
+ u16 pad1;
+ s16 incr_dst;
+ u16 pad2;
+ s16 incr_src;
+};
+
+
+/* ======================================================================== */
+/* Task support code */
+/* ======================================================================== */
+
+struct bcom_task *
+bcom_ata_init(int queue_len, int maxbufsize)
+{
+ struct bcom_task *tsk;
+ struct bcom_ata_var *var;
+ struct bcom_ata_inc *inc;
+
+ tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0);
+ if (!tsk)
+ return NULL;
+
+ tsk->flags = BCOM_FLAGS_NONE;
+
+ bcom_ata_reset(tsk);
+
+ var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum);
+ inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
+
+ if (bcom_load_image(tsk->tasknum, bcom_ata_task)) {
+ bcom_task_release(tsk);
+ return NULL;
+ }
+
+ var->enable = bcom->regs_base +
+ offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
+ var->bd_base = tsk->bd_pa;
+ var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
+ var->bd_start = tsk->bd_pa;
+ var->buffer_size = maxbufsize;
+
+ /* Configure some stuff */
+ bcom_set_task_pragma(tsk->tasknum, BCOM_ATA_PRAGMA);
+ bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
+
+ out_8(&bcom->regs->ipr[BCOM_INITIATOR_ATA_RX], BCOM_IPR_ATA_RX);
+ out_8(&bcom->regs->ipr[BCOM_INITIATOR_ATA_TX], BCOM_IPR_ATA_TX);
+
+ out_be32(&bcom->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
+
+ return tsk;
+}
+
+void bcom_ata_rx_prepare(struct bcom_task *tsk)
+{
+ struct bcom_ata_inc *inc;
+
+ inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
+
+ inc->incr_bytes = -(s16)sizeof(u32);
+ inc->incr_src = 0;
+ inc->incr_dst = sizeof(u32);
+
+ bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_RX);
+}
+
+void bcom_ata_tx_prepare(struct bcom_task *tsk)
+{
+ struct bcom_ata_inc *inc;
+
+ inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
+
+ inc->incr_bytes = -(s16)sizeof(u32);
+ inc->incr_src = sizeof(u32);
+ inc->incr_dst = 0;
+
+ bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_TX);
+}
+
+void bcom_ata_reset_bd(struct bcom_task *tsk)
+{
+ struct bcom_ata_var *var;
+
+ /* Reset all BD */
+ memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
+
+ tsk->index = 0;
+ tsk->outdex = 0;
+
+ var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum);
+ var->bd_start = var->bd_base;
+}
+
+void bcom_ata_release(struct bcom_task *tsk)
+{
+ /* Nothing special for the ATA tasks */
+ bcom_task_release(tsk);
+}
+
+
+EXPORT_SYMBOL(bcom_ata_init);
+EXPORT_SYMBOL(bcom_ata_rx_prepare);
+EXPORT_SYMBOL(bcom_ata_tx_prepare);
+EXPORT_SYMBOL(bcom_ata_reset);
+EXPORT_SYMBOL(bcom_ata_release);
+
+MODULE_DESCRIPTION("BestComm ATA task driver");
+MODULE_AUTHOR("John Rigby");
+MODULE_LICENSE("GPL v2");
+
diff --git a/arch/powerpc/sysdev/bestcomm/ata.h b/arch/powerpc/sysdev/bestcomm/ata.h
new file mode 100644
index 0000000..1098276
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/ata.h
@@ -0,0 +1,37 @@
+/*
+ * Header for Bestcomm ATA task driver
+ *
+ *
+ * Copyright (C) 2006 Freescale - John Rigby
+ * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ */
+
+#ifndef __BESTCOMM_ATA_H__
+#define __BESTCOMM_ATA_H__
+
+
+struct bcom_ata_bd {
+ u32 status;
+ u32 dst_pa;
+ u32 src_pa;
+};
+
+extern struct bcom_task *
+bcom_ata_init(int queue_len, int maxbufsize);
+
+extern void
+bcom_ata_rx_prepare(struct bcom_task *tsk);
+
+extern void
+bcom_ata_tx_prepare(struct bcom_task *tsk);
+
+extern void
+bcom_ata_reset_bd(struct bcom_task *tsk);
+
+
+#endif /* __BESTCOMM_ATA_H__ */
+
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c b/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c
new file mode 100644
index 0000000..779cebb
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c
@@ -0,0 +1,61 @@
+/*
+ * Bestcomm ATA task microcode
+ *
+ * Created based on bestcom/code_dma/image_rtos1/dma_image.hex
+ */
+
+#include <asm/types.h>
+
+/*
+ * The header consists of the following fields:
+ * u32 magic;
+ * u8 desc_size;
+ * u8 var_size;
+ * u8 inc_size;
+ * u8 first_var;
+ * u8 reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+ */
+
+u32 bcom_ata_task[] = {
+ /* header */
+ 0x4243544b,
+ 0x0e060709,
+ 0x00000000,
+ 0x00000000,
+
+ /* Task descriptors */
+ 0x8198009b, /* LCD: idx0 = var3; idx0 <= var2; idx0 += inc3 */
+ 0x13e00c08, /* DRD1A: var3 = var1; FN=0 MORE init=31 WS=0 RS=0 */
+ 0xb8000264, /* LCD: idx1 = *idx0, idx2 = var0; idx1 < var9; idx1 += inc4, idx2 += inc4 */
+ 0x10000f00, /* DRD1A: var3 = idx0; 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 */
+ 0x0c8cfc8a, /* DRD2B1: *idx2 = EU3(); EU3(*idx2,var10) */
+ 0xd8988240, /* LCDEXT: idx1 = idx1; idx1 > var9; idx1 += inc0 */
+ 0xf845e011, /* LCDEXT: idx2 = *(idx0 + var00000015); ; idx2 += inc2 */
+ 0xb845e00a, /* LCD: idx3 = *(idx0 + var00000019); ; idx3 += inc1 */
+ 0x0bfecf90, /* DRD1A: *idx3 = *idx2; FN=0 TFD init=31 WS=3 RS=3 */
+ 0x9898802d, /* LCD: idx1 = idx1; idx1 once var0; idx1 += inc5 */
+ 0x64000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 INT EXT init=0 WS=0 RS=0 */
+ 0x0c0cf849, /* DRD2B1: *idx0 = EU3(); EU3(idx1,var9) */
+ 0x000001f8, /* NOP */
+
+ /* VAR[9]-VAR[14] */
+ 0x40000000,
+ 0x7fff7fff,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+
+ /* INC[0]-INC[6] */
+ 0x40000000,
+ 0xe0000000,
+ 0xe0000000,
+ 0xa000000c,
+ 0x20000000,
+ 0x00000000,
+ 0x00000000,
+};
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 6/9] powerpc: BestcComm FEC task support
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 7/9] powerpc: BestcComm GenBD " Sylvain Munaut
2007-05-12 23:30 ` [PATCH 5/9] powerpc: BestcComm ATA task support Arnd Bergmann
1 sibling, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
This is the microcode for the FEC task and the associated
support code.
The microcode itself comes directly from the offical
API (v2.2)
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/sysdev/bestcomm/Kconfig | 7 +
arch/powerpc/sysdev/bestcomm/Makefile | 2 +
arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c | 72 ++++++
arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c | 85 +++++++
arch/powerpc/sysdev/bestcomm/fec.c | 271 +++++++++++++++++++++++
arch/powerpc/sysdev/bestcomm/fec.h | 48 ++++
6 files changed, 485 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c
create mode 100644 arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c
create mode 100644 arch/powerpc/sysdev/bestcomm/fec.c
create mode 100644 arch/powerpc/sysdev/bestcomm/fec.h
diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig
index 9d087ce..831763b 100644
--- a/arch/powerpc/sysdev/bestcomm/Kconfig
+++ b/arch/powerpc/sysdev/bestcomm/Kconfig
@@ -23,3 +23,10 @@ config PPC_BESTCOMM_ATA
help
This option enables the support for the ATA task.
+config PPC_BESTCOMM_FEC
+ tristate "Bestcomm FEC tasks support"
+ depends on PPC_BESTCOMM
+ default n
+ help
+ This option enables the support for the FEC tasks.
+
diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile
index b7a6a40..537d174 100644
--- a/arch/powerpc/sysdev/bestcomm/Makefile
+++ b/arch/powerpc/sysdev/bestcomm/Makefile
@@ -4,7 +4,9 @@
bestcomm-core-objs := bestcomm.o sram.o
bestcomm-ata-objs := ata.o bcom_ata_task.o
+bestcomm-fec-objs := fec.o bcom_fec_rx_task.o bcom_fec_tx_task.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o
+obj-$(CONFIG_PPC_BESTCOMM_FEC) += bestcomm-fec.o
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c
new file mode 100644
index 0000000..48bae92
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c
@@ -0,0 +1,72 @@
+/*
+ * Bestcomm FEC RX task microcode
+ *
+ * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 22 11:19:38 2005 GMT
+ */
+
+#include <asm/types.h>
+
+/*
+ * The header consists of the following fields:
+ * u32 magic;
+ * u8 desc_size;
+ * u8 var_size;
+ * u8 inc_size;
+ * u8 first_var;
+ * u8 reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+ */
+
+u32 bcom_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/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c
new file mode 100644
index 0000000..8c5aa83
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c
@@ -0,0 +1,85 @@
+/*
+ * Bestcomm FEC TX task microcode
+ *
+ * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 22 11:19:29 2005 GMT
+ */
+
+#include <asm/types.h>
+
+/*
+ * The header consists of the following fields:
+ * u32 magic;
+ * u8 desc_size;
+ * u8 var_size;
+ * u8 inc_size;
+ * u8 first_var;
+ * u8 reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+ */
+
+u32 bcom_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/powerpc/sysdev/bestcomm/fec.c b/arch/powerpc/sysdev/bestcomm/fec.c
new file mode 100644
index 0000000..56ff560
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/fec.c
@@ -0,0 +1,271 @@
+/*
+ * Bestcomm FEC tasks driver
+ *
+ *
+ * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "bestcomm.h"
+#include "bestcomm_priv.h"
+#include "fec.h"
+
+
+/* ======================================================================== */
+/* Task image/var/inc */
+/* ======================================================================== */
+
+/* fec tasks images */
+extern u32 bcom_fec_rx_task[];
+extern u32 bcom_fec_tx_task[];
+
+/* rx task vars that need to be set before enabling the task */
+struct bcom_fec_rx_var {
+ u32 enable; /* (u16*) address of task's control register */
+ u32 fifo; /* (u32*) address of fec's fifo */
+ u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
+ u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
+ u32 bd_start; /* (struct bcom_bd*) current bd */
+ u32 buffer_size; /* size of receive buffer */
+};
+
+/* rx task incs that need to be set before enabling the task */
+struct bcom_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 bcom_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 bcom_bd*) beginning of ring buffer */
+ u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
+ u32 bd_start; /* (struct bcom_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 bcom_fec_tx_inc {
+ u16 pad0;
+ s16 incr_bytes;
+ u16 pad1;
+ s16 incr_src;
+ u16 pad2;
+ s16 incr_src_ma;
+};
+
+/* private structure in the task */
+struct bcom_fec_priv {
+ phys_addr_t fifo;
+ int maxbufsize;
+};
+
+
+/* ======================================================================== */
+/* Task support code */
+/* ======================================================================== */
+
+struct bcom_task *
+bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize)
+{
+ struct bcom_task *tsk;
+ struct bcom_fec_priv *priv;
+
+ tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd),
+ sizeof(struct bcom_fec_priv));
+ if (!tsk)
+ return NULL;
+
+ tsk->flags = BCOM_FLAGS_NONE;
+
+ priv = tsk->priv;
+ priv->fifo = fifo;
+ priv->maxbufsize = maxbufsize;
+
+ if (bcom_fec_rx_reset(tsk)) {
+ bcom_task_release(tsk);
+ return NULL;
+ }
+
+ return tsk;
+}
+
+int
+bcom_fec_rx_reset(struct bcom_task *tsk)
+{
+ struct bcom_fec_priv *priv = tsk->priv;
+ struct bcom_fec_rx_var *var;
+ struct bcom_fec_rx_inc *inc;
+
+ /* Shutdown the task */
+ bcom_disable_task(tsk->tasknum);
+
+ /* Reset the microcode */
+ var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum);
+ inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum);
+
+ if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task))
+ return -1;
+
+ var->enable = bcom->regs_base +
+ offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
+ var->fifo = (u32) priv->fifo;
+ var->bd_base = tsk->bd_pa;
+ var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
+ var->bd_start = tsk->bd_pa;
+ var->buffer_size = priv->maxbufsize;
+
+ inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */
+ inc->incr_dst = sizeof(u32); /* task image, but we stick */
+ inc->incr_dst_ma= sizeof(u8); /* to the official ones */
+
+ /* Reset the BDs */
+ tsk->index = 0;
+ tsk->outdex = 0;
+
+ memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
+
+ /* Configure some stuff */
+ bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA);
+ bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
+
+ out_8(&bcom->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX);
+
+ out_be32(&bcom->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
+
+ return 0;
+}
+
+void
+bcom_fec_rx_release(struct bcom_task *tsk)
+{
+ /* Nothing special for the FEC tasks */
+ bcom_task_release(tsk);
+}
+
+
+
+ /* 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 = bcom_task_num_descs(tasknum);
+ desc = bcom_task_desc(tasknum) + num_descs - 1;
+ drd_count = 0;
+ for (i=0; i<num_descs; i++, desc--)
+ if (bcom_desc_is_drd(*desc) && ++drd_count == 3)
+ break;
+ return desc;
+}
+
+struct bcom_task *
+bcom_fec_tx_init(int queue_len, phys_addr_t fifo)
+{
+ struct bcom_task *tsk;
+ struct bcom_fec_priv *priv;
+
+ tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd),
+ sizeof(struct bcom_fec_priv));
+ if (!tsk)
+ return NULL;
+
+ tsk->flags = BCOM_FLAGS_ENABLE_TASK;
+
+ priv = tsk->priv;
+ priv->fifo = fifo;
+
+ if (bcom_fec_tx_reset(tsk)) {
+ bcom_task_release(tsk);
+ return NULL;
+ }
+
+ return tsk;
+}
+
+int
+bcom_fec_tx_reset(struct bcom_task *tsk)
+{
+ struct bcom_fec_priv *priv = tsk->priv;
+ struct bcom_fec_tx_var *var;
+ struct bcom_fec_tx_inc *inc;
+
+ /* Shutdown the task */
+ bcom_disable_task(tsk->tasknum);
+
+ /* Reset the microcode */
+ var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum);
+ inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum);
+
+ if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task))
+ return -1;
+
+ var->enable = bcom->regs_base +
+ offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
+ var->fifo = (u32) priv->fifo;
+ var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum));
+ var->bd_base = tsk->bd_pa;
+ var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
+ var->bd_start = tsk->bd_pa;
+
+ inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */
+ inc->incr_src = sizeof(u32); /* task image, but we stick */
+ inc->incr_src_ma= sizeof(u8); /* to the official ones */
+
+ /* Reset the BDs */
+ tsk->index = 0;
+ tsk->outdex = 0;
+
+ memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
+
+ /* Configure some stuff */
+ bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA);
+ bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
+
+ out_8(&bcom->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX);
+
+ out_be32(&bcom->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
+
+ return 0;
+}
+
+void
+bcom_fec_tx_release(struct bcom_task *tsk)
+{
+ /* Nothing special for the FEC tasks */
+ bcom_task_release(tsk);
+}
+
+
+EXPORT_SYMBOL(bcom_fec_rx_init);
+EXPORT_SYMBOL(bcom_fec_rx_reset);
+EXPORT_SYMBOL(bcom_fec_rx_release);
+EXPORT_SYMBOL(bcom_fec_tx_init);
+EXPORT_SYMBOL(bcom_fec_tx_reset);
+EXPORT_SYMBOL(bcom_fec_tx_release);
+
+MODULE_DESCRIPTION("BestComm FEC tasks driver");
+MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/arch/powerpc/sysdev/bestcomm/fec.h b/arch/powerpc/sysdev/bestcomm/fec.h
new file mode 100644
index 0000000..fa880ca
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/fec.h
@@ -0,0 +1,48 @@
+/*
+ * Header for Bestcomm FEC tasks driver
+ *
+ *
+ * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
+ *
+ * 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.
+ */
+
+#ifndef __BESTCOMM_FEC_H__
+#define __BESTCOMM_FEC_H__
+
+
+struct bcom_fec_bd {
+ u32 status;
+ u32 skb_pa;
+};
+
+#define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */
+#define BCOM_FEC_TX_BD_INT 0x04000000ul /* interrupt */
+
+
+extern struct bcom_task *
+bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize);
+
+extern int
+bcom_fec_rx_reset(struct bcom_task *tsk);
+
+extern void
+bcom_fec_rx_release(struct bcom_task *tsk);
+
+
+extern struct bcom_task *
+bcom_fec_tx_init(int queue_len, phys_addr_t fifo);
+
+extern int
+bcom_fec_tx_reset(struct bcom_task *tsk);
+
+extern void
+bcom_fec_tx_release(struct bcom_task *tsk);
+
+
+#endif /* __BESTCOMM_FEC_H__ */
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 7/9] powerpc: BestcComm GenBD task support
2007-05-12 20:31 ` [PATCH 6/9] powerpc: BestcComm FEC " Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 8/9] drivers/net: Add support for Freescale MPC5200 SoC internal FEC Sylvain Munaut
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
This is the microcode for the GenBD task and the associated
support code. This is a generic task that copy data to/from
a hardware FIFO. This is currently locked to 32bits wide
access but could be extended as needed.
The microcode itself comes directly from the offical
API (v2.2)
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
arch/powerpc/sysdev/bestcomm/Kconfig | 7 +
arch/powerpc/sysdev/bestcomm/Makefile | 2 +
arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c | 62 +++++
arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c | 68 +++++
arch/powerpc/sysdev/bestcomm/gen_bd.c | 261 ++++++++++++++++++++
arch/powerpc/sysdev/bestcomm/gen_bd.h | 48 ++++
6 files changed, 448 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c
create mode 100644 arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c
create mode 100644 arch/powerpc/sysdev/bestcomm/gen_bd.c
create mode 100644 arch/powerpc/sysdev/bestcomm/gen_bd.h
diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig
index 831763b..57cc565 100644
--- a/arch/powerpc/sysdev/bestcomm/Kconfig
+++ b/arch/powerpc/sysdev/bestcomm/Kconfig
@@ -30,3 +30,10 @@ config PPC_BESTCOMM_FEC
help
This option enables the support for the FEC tasks.
+config PPC_BESTCOMM_GEN_BD
+ tristate "Bestcomm GenBD tasks support"
+ depends on PPC_BESTCOMM
+ default n
+ help
+ This option enables the support for the GenBD tasks.
+
diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile
index 537d174..aed2df2 100644
--- a/arch/powerpc/sysdev/bestcomm/Makefile
+++ b/arch/powerpc/sysdev/bestcomm/Makefile
@@ -5,8 +5,10 @@
bestcomm-core-objs := bestcomm.o sram.o
bestcomm-ata-objs := ata.o bcom_ata_task.o
bestcomm-fec-objs := fec.o bcom_fec_rx_task.o bcom_fec_tx_task.o
+bestcomm-gen-bd-objs := gen_bd.o bcom_gen_bd_rx_task.o bcom_gen_bd_tx_task.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o
obj-$(CONFIG_PPC_BESTCOMM_FEC) += bestcomm-fec.o
+obj-$(CONFIG_PPC_BESTCOMM_GEN_BD) += bestcomm-gen-bd.o
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c
new file mode 100644
index 0000000..f356886
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c
@@ -0,0 +1,62 @@
+/*
+ * Bestcomm GenBD RX task microcode
+ *
+ * Copyright (C) 2006 AppSpec Computer Technologies Corp.
+ * Jeff Gibbons <jeff.gibbons@appspec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 4 10:14:12 2006 GMT
+ *
+ */
+
+#include <asm/types.h>
+
+/*
+ * The header consists of the following fields:
+ * u32 magic;
+ * u8 desc_size;
+ * u8 var_size;
+ * u8 inc_size;
+ * u8 first_var;
+ * u8 reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+ */
+
+u32 bcom_gen_bd_rx_task[] = {
+ /* header */
+ 0x4243544b,
+ 0x0d020409,
+ 0x00000000,
+ 0x00000000,
+
+ /* Task descriptors */
+ 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */
+ 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */
+ 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */
+ 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) */
+ 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */
+ 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */
+ 0x07fecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=31 WS=3 RS=3 */
+ 0x99190024, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc4 */
+ 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
+ 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */
+ 0x000001f8, /* NOP */
+
+ /* VAR[9]-VAR[10] */
+ 0x40000000,
+ 0x7fff7fff,
+
+ /* INC[0]-INC[3] */
+ 0x40000000,
+ 0xe0000000,
+ 0xa0000008,
+ 0x20000000,
+};
+
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c
new file mode 100644
index 0000000..7460c39
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c
@@ -0,0 +1,68 @@
+/*
+ * Bestcomm GenBD TX task microcode
+ *
+ * Copyright (C) 2006 AppSpec Computer Technologies Corp.
+ * Jeff Gibbons <jeff.gibbons@appspec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
+ * on Tue Mar 4 10:14:12 2006 GMT
+ *
+ */
+
+#include <asm/types.h>
+
+/*
+ * The header consists of the following fields:
+ * u32 magic;
+ * u8 desc_size;
+ * u8 var_size;
+ * u8 inc_size;
+ * u8 first_var;
+ * u8 reserved[8];
+ *
+ * The size fields contain the number of 32-bit words.
+ */
+
+u32 bcom_gen_bd_tx_task[] = {
+ /* header */
+ 0x4243544b,
+ 0x0f040609,
+ 0x00000000,
+ 0x00000000,
+
+ /* Task descriptors */
+ 0x800220e3, /* LCD: idx0 = var0, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */
+ 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */
+ 0xb8808264, /* LCD: idx2 = *idx1, idx3 = var1; 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) */
+ 0xd9190300, /* LCDEXT: idx2 = idx2; idx2 > var12; idx2 += inc0 */
+ 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */
+ 0x03fec398, /* DRD1A: *idx0 = *idx3; FN=0 init=31 WS=3 RS=3 */
+ 0x9919826a, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc5, idx3 += inc2 */
+ 0x0feac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD INT init=31 WS=1 RS=1 */
+ 0x99190036, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */
+ 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
+ 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */
+ 0x000001f8, /* NOP */
+
+ /* VAR[9]-VAR[12] */
+ 0x40000000,
+ 0x7fff7fff,
+ 0x00000000,
+ 0x40000004,
+
+ /* INC[0]-INC[5] */
+ 0x40000000,
+ 0xe0000000,
+ 0xe0000000,
+ 0xa0000008,
+ 0x20000000,
+ 0x4000ffff,
+};
+
diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.c b/arch/powerpc/sysdev/bestcomm/gen_bd.c
new file mode 100644
index 0000000..b221bbc
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/gen_bd.c
@@ -0,0 +1,261 @@
+/*
+ * Driver for MPC52xx processor BestComm General Buffer Descriptor
+ *
+ * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2006 AppSpec Computer Technologies Corp.
+ * Jeff Gibbons <jeff.gibbons@appspec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#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 "bestcomm_priv.h"
+#include "gen_bd.h"
+
+
+/* ======================================================================== */
+/* Task image/var/inc */
+/* ======================================================================== */
+
+/* gen_bd tasks images */
+extern u32 bcom_gen_bd_rx_task[];
+extern u32 bcom_gen_bd_tx_task[];
+
+/* rx task vars that need to be set before enabling the task */
+struct bcom_gen_bd_rx_var {
+ u32 enable; /* (u16*) address of task's control register */
+ u32 fifo; /* (u32*) address of gen_bd's fifo */
+ u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
+ u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
+ u32 bd_start; /* (struct bcom_bd*) current bd */
+ u32 buffer_size; /* size of receive buffer */
+};
+
+/* rx task incs that need to be set before enabling the task */
+struct bcom_gen_bd_rx_inc {
+ u16 pad0;
+ s16 incr_bytes;
+ u16 pad1;
+ s16 incr_dst;
+};
+
+/* tx task vars that need to be set before enabling the task */
+struct bcom_gen_bd_tx_var {
+ u32 fifo; /* (u32*) address of gen_bd's fifo */
+ u32 enable; /* (u16*) address of task's control register */
+ u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
+ u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
+ u32 bd_start; /* (struct bcom_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 bcom_gen_bd_tx_inc {
+ u16 pad0;
+ s16 incr_bytes;
+ u16 pad1;
+ s16 incr_src;
+ u16 pad2;
+ s16 incr_src_ma;
+};
+
+/* private structure */
+struct bcom_gen_bd_priv {
+ phys_addr_t fifo;
+ int initiator;
+ int ipr;
+ int maxbufsize;
+};
+
+
+/* ======================================================================== */
+/* Task support code */
+/* ======================================================================== */
+
+struct bcom_task *
+bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo,
+ int initiator, int ipr, int maxbufsize)
+{
+ struct bcom_task *tsk;
+ struct bcom_gen_bd_priv *priv;
+
+ tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd),
+ sizeof(struct bcom_gen_bd_priv));
+ if (!tsk)
+ return NULL;
+
+ tsk->flags = BCOM_FLAGS_NONE;
+
+ priv = tsk->priv;
+ priv->fifo = fifo;
+ priv->initiator = initiator;
+ priv->ipr = ipr;
+ priv->maxbufsize = maxbufsize;
+
+ if (bcom_gen_bd_rx_reset(tsk)) {
+ bcom_task_release(tsk);
+ return NULL;
+ }
+
+ return tsk;
+}
+
+int
+bcom_gen_bd_rx_reset(struct bcom_task *tsk)
+{
+ struct bcom_gen_bd_priv *priv = tsk->priv;
+ struct bcom_gen_bd_rx_var *var;
+ struct bcom_gen_bd_rx_inc *inc;
+
+ /* Shutdown the task */
+ bcom_disable_task(tsk->tasknum);
+
+ /* Reset the microcode */
+ var = (struct bcom_gen_bd_rx_var *) bcom_task_var(tsk->tasknum);
+ inc = (struct bcom_gen_bd_rx_inc *) bcom_task_inc(tsk->tasknum);
+
+ if (bcom_load_image(tsk->tasknum, bcom_gen_bd_rx_task))
+ return -1;
+
+ var->enable = bcom->regs_base +
+ offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
+ var->fifo = (u32) priv->fifo;
+ var->bd_base = tsk->bd_pa;
+ var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
+ var->bd_start = tsk->bd_pa;
+ var->buffer_size = priv->maxbufsize;
+
+ inc->incr_bytes = -(s16)sizeof(u32);
+ inc->incr_dst = sizeof(u32);
+
+ /* Reset the BDs */
+ tsk->index = 0;
+ tsk->outdex = 0;
+
+ memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
+
+ /* Configure some stuff */
+ bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_RX_BD_PRAGMA);
+ bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
+
+ out_8(&bcom->regs->ipr[priv->initiator], priv->ipr);
+ bcom_set_initiator(tsk->tasknum, priv->initiator);
+
+ out_be32(&bcom->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
+
+ return 0;
+}
+
+void
+bcom_gen_bd_rx_release(struct bcom_task *tsk)
+{
+ /* Nothing special for the GenBD tasks */
+ bcom_task_release(tsk);
+}
+
+
+extern struct bcom_task *
+bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo,
+ int initiator, int ipr)
+{
+ struct bcom_task *tsk;
+ struct bcom_gen_bd_priv *priv;
+
+ tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd),
+ sizeof(struct bcom_gen_bd_priv));
+ if (!tsk)
+ return NULL;
+
+ tsk->flags = BCOM_FLAGS_NONE;
+
+ priv = tsk->priv;
+ priv->fifo = fifo;
+ priv->initiator = initiator;
+ priv->ipr = ipr;
+
+ if (bcom_gen_bd_tx_reset(tsk)) {
+ bcom_task_release(tsk);
+ return NULL;
+ }
+
+ return tsk;
+}
+
+int
+bcom_gen_bd_tx_reset(struct bcom_task *tsk)
+{
+ struct bcom_gen_bd_priv *priv = tsk->priv;
+ struct bcom_gen_bd_tx_var *var;
+ struct bcom_gen_bd_tx_inc *inc;
+
+ /* Shutdown the task */
+ bcom_disable_task(tsk->tasknum);
+
+ /* Reset the microcode */
+ var = (struct bcom_gen_bd_tx_var *) bcom_task_var(tsk->tasknum);
+ inc = (struct bcom_gen_bd_tx_inc *) bcom_task_inc(tsk->tasknum);
+
+ if (bcom_load_image(tsk->tasknum, bcom_gen_bd_tx_task))
+ return -1;
+
+ var->enable = bcom->regs_base +
+ offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
+ var->fifo = (u32) priv->fifo;
+ var->bd_base = tsk->bd_pa;
+ var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
+ var->bd_start = tsk->bd_pa;
+
+ inc->incr_bytes = -(s16)sizeof(u32);
+ inc->incr_src = sizeof(u32);
+ inc->incr_src_ma = sizeof(u8);
+
+ /* Reset the BDs */
+ tsk->index = 0;
+ tsk->outdex = 0;
+
+ memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
+
+ /* Configure some stuff */
+ bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_TX_BD_PRAGMA);
+ bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
+
+ out_8(&bcom->regs->ipr[priv->initiator], priv->ipr);
+ bcom_set_initiator(tsk->tasknum, priv->initiator);
+
+ out_be32(&bcom->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
+
+ return 0;
+}
+
+void
+bcom_gen_bd_tx_release(struct bcom_task *tsk)
+{
+ /* Nothing special for the GenBD tasks */
+ bcom_task_release(tsk);
+}
+
+
+EXPORT_SYMBOL(bcom_gen_bd_rx_init);
+EXPORT_SYMBOL(bcom_gen_bd_rx_reset);
+EXPORT_SYMBOL(bcom_gen_bd_rx_release);
+EXPORT_SYMBOL(bcom_gen_bd_tx_init);
+EXPORT_SYMBOL(bcom_gen_bd_tx_reset);
+EXPORT_SYMBOL(bcom_gen_bd_tx_release);
+
+MODULE_DESCRIPTION("BestComm General Buffer Descriptor tasks driver");
+MODULE_AUTHOR("Jeff Gibbons <jeff.gibbons@appspec.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.h b/arch/powerpc/sysdev/bestcomm/gen_bd.h
new file mode 100644
index 0000000..5b6fa80
--- /dev/null
+++ b/arch/powerpc/sysdev/bestcomm/gen_bd.h
@@ -0,0 +1,48 @@
+/*
+ * Header for Bestcomm General Buffer Descriptor tasks driver
+ *
+ *
+ * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2006 AppSpec Computer Technologies Corp.
+ * Jeff Gibbons <jeff.gibbons@appspec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ *
+ */
+
+#ifndef __BESTCOMM_GEN_BD_H__
+#define __BESTCOMM_GEN_BD_H__
+
+struct bcom_gen_bd {
+ u32 status;
+ u32 buf_pa;
+};
+
+
+extern struct bcom_task *
+bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo,
+ int initiator, int ipr, int maxbufsize);
+
+extern int
+bcom_gen_bd_rx_reset(struct bcom_task *tsk);
+
+extern void
+bcom_gen_bd_rx_release(struct bcom_task *tsk);
+
+
+extern struct bcom_task *
+bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo,
+ int initiator, int ipr);
+
+extern int
+bcom_gen_bd_tx_reset(struct bcom_task *tsk);
+
+extern void
+bcom_gen_bd_tx_release(struct bcom_task *tsk);
+
+
+#endif /* __BESTCOMM_GEN_BD_H__ */
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 8/9] drivers/net: Add support for Freescale MPC5200 SoC internal FEC.
2007-05-12 20:31 ` [PATCH 7/9] powerpc: BestcComm GenBD " Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
2007-05-12 20:31 ` [PATCH 9/9] sound: Add support for Freescale MPC5200 AC97 interface Sylvain Munaut
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
Not quite a clean driver, but it get things done.
Only included to be able to test functionalityi/usage of
the BestComm driver.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
drivers/net/Kconfig | 1 +
drivers/net/Makefile | 1 +
drivers/net/fec_mpc52xx/Kconfig | 24 ++
drivers/net/fec_mpc52xx/Makefile | 7 +
drivers/net/fec_mpc52xx/fec.c | 813 +++++++++++++++++++++++++++++++++++++
drivers/net/fec_mpc52xx/fec.h | 306 ++++++++++++++
drivers/net/fec_mpc52xx/fec_phy.c | 526 ++++++++++++++++++++++++
drivers/net/fec_mpc52xx/fec_phy.h | 73 ++++
8 files changed, 1751 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/fec_mpc52xx/Kconfig
create mode 100644 drivers/net/fec_mpc52xx/Makefile
create mode 100644 drivers/net/fec_mpc52xx/fec.c
create mode 100644 drivers/net/fec_mpc52xx/fec.h
create mode 100644 drivers/net/fec_mpc52xx/fec_phy.c
create mode 100644 drivers/net/fec_mpc52xx/fec_phy.h
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index fb99cd4..df8df41 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1890,6 +1890,7 @@ config NE_H8300
controller on the Renesas H8/300 processor.
source "drivers/net/fec_8xx/Kconfig"
+source "drivers/net/fec_mpc52xx/Kconfig"
source "drivers/net/fs_enet/Kconfig"
endmenu
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a77affa..72e092c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -196,6 +196,7 @@ obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_FEC_8XX) += fec_8xx/
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx/
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
diff --git a/drivers/net/fec_mpc52xx/Kconfig b/drivers/net/fec_mpc52xx/Kconfig
new file mode 100644
index 0000000..1043a1a
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/Kconfig
@@ -0,0 +1,24 @@
+menu "MPC5200 Networking Options"
+ depends PPC_MPC52xx && NET_ETHERNET
+
+config FEC_MPC52xx
+ tristate "FEC Ethernet"
+ depends on NET_ETHERNET
+ select PPC_BESTCOMM
+ select PPC_BESTCOMM_FEC
+ select CRC32
+ ---help---
+ This option enables support for the MPC5200's on-chip
+ Fast Ethernet Controller
+
+config USE_MDIO
+ bool "Use external Ethernet MII PHY"
+ select MII
+ depends FEC_MPC52xx
+ ---help---
+ The MPC5200's FEC can connect to the Ethernet either with
+ an external MII PHY chip or 10 Mbps 7-wire interface
+ (Motorola? industry standard).
+ If your board uses an external PHY, say y, else n.
+
+endmenu
diff --git a/drivers/net/fec_mpc52xx/Makefile b/drivers/net/fec_mpc52xx/Makefile
new file mode 100644
index 0000000..900c2c1
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
+
+fec_mpc52xx-objs := fec.o
+
+ifeq ($(CONFIG_USE_MDIO),y)
+fec_mpc52xx-objs += fec_phy.o
+endif
diff --git a/drivers/net/fec_mpc52xx/fec.c b/drivers/net/fec_mpc52xx/fec.c
new file mode 100644
index 0000000..416bac7
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec.c
@@ -0,0 +1,813 @@
+/*
+ * drivers/net/fec_mpc52xx/fec.c
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and
+ * now maintained by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyrigth (C) 2003-2004 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.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/fec.h>
+
+#include "fec_phy.h"
+#include "fec.h"
+
+#define DRIVER_NAME "mpc52xx-fec"
+
+static irqreturn_t fec_interrupt(int, void *);
+static irqreturn_t fec_rx_interrupt(int, void *);
+static irqreturn_t fec_tx_interrupt(int, void *);
+static struct net_device_stats *fec_get_stats(struct net_device *);
+static void fec_set_multicast_list(struct net_device *dev);
+static void fec_reinit(struct net_device *dev);
+
+static u8 mpc52xx_fec_mac_addr[6];
+static u8 null_mac[6];
+
+static void fec_tx_timeout(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ priv->stats.tx_errors++;
+
+ if (!priv->tx_full)
+ netif_wake_queue(dev);
+}
+
+static void fec_set_paddr(struct net_device *dev, u8 *mac)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ out_be32(&fec->paddr1, *(u32*)(&mac[0]));
+ out_be32(&fec->paddr2, (*(u16*)(&mac[4]) << 16) | 0x8808);
+}
+
+static void fec_get_paddr(struct net_device *dev, u8 *mac)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ *(u32*)(&mac[0]) = in_be32(&fec->paddr1);
+ *(u16*)(&mac[4]) = in_be32(&fec->paddr2) >> 16;
+}
+
+static int fec_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sock = (struct sockaddr *)addr;
+
+ memcpy(dev->dev_addr, sock->sa_data, dev->addr_len);
+
+ fec_set_paddr(dev, sock->sa_data);
+ return 0;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change. This happens on fifo errors or when switching between half
+ * and full duplex.
+ */
+static void fec_restart(struct net_device *dev, int duplex)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ u32 rcntrl;
+ u32 tcntrl;
+ int i;
+
+ out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & 0x700000);
+ out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & 0x700000);
+ out_be32(&fec->reset_cntrl, 0x1000000);
+
+ /* Whack a reset. We should wait for this. */
+ out_be32(&fec->ecntrl, FEC_ECNTRL_RESET);
+ for (i = 0; i < FEC_RESET_DELAY; ++i) {
+ if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0)
+ break;
+ udelay(1);
+ }
+ if (i == FEC_RESET_DELAY)
+ printk (KERN_ERR DRIVER_NAME ": FEC Reset timeout!\n");
+
+ /* Set station address. */
+ fec_set_paddr(dev, dev->dev_addr);
+
+ fec_set_multicast_list(dev);
+
+ rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */
+ rcntrl |= FEC_RCNTRL_FCE;
+ rcntrl |= MII_RCNTL_MODE;
+ if (duplex)
+ tcntrl = FEC_TCNTRL_FDEN; /* FD enable */
+ else {
+ rcntrl |= FEC_RCNTRL_DRT;
+ tcntrl = 0;
+ }
+ out_be32(&fec->r_cntrl, rcntrl);
+ out_be32(&fec->x_cntrl, tcntrl);
+
+ set_phy_speed(fec, priv->phy_speed);
+
+ priv->full_duplex = duplex;
+
+ /* Clear any outstanding interrupt. */
+ out_be32(&fec->ievent, 0xffffffff); /* clear intr events */
+
+ /* Enable interrupts we wish to service.
+ */
+ out_be32(&fec->imask, FEC_IMASK_ENABLE);
+
+ /* And last, enable the transmit and receive processing.
+ */
+ out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN);
+ out_be32(&fec->r_des_active, 0x01000000);
+
+ /* The tx ring is no longer full. */
+ if (priv->tx_full)
+ {
+ priv->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+}
+
+static void fec_free_rx_buffers(struct bcom_task *s)
+{
+ struct sk_buff *skb;
+
+ while (!bcom_queue_empty(s)) {
+ skb = bcom_retrieve_buffer(s, NULL, NULL);
+ kfree_skb(skb);
+ }
+}
+
+static int fec_open(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ bcom_fec_rx_reset(priv->rx_dmatsk);
+ bcom_fec_tx_reset(priv->tx_dmatsk);
+
+ while (!bcom_queue_full(priv->rx_dmatsk)) {
+ struct sk_buff *skb;
+ struct bcom_fec_bd *bd;
+
+ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+ if (skb == 0)
+ goto eagain;
+
+ /* zero out the initial receive buffers to aid debugging */
+ memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
+
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = virt_to_phys(skb->data);
+
+ bcom_submit_next_buffer(priv->rx_dmatsk, skb);
+ }
+
+ fec_set_paddr(dev, dev->dev_addr);
+
+ if (fec_mii_wait(dev) != 0)
+ return -ENODEV;
+
+ bcom_enable(priv->rx_dmatsk);
+ bcom_enable(priv->tx_dmatsk);
+
+ netif_start_queue(dev);
+
+ return 0;
+
+eagain:
+ printk(KERN_ERR "fec_open: failed\n");
+
+ fec_free_rx_buffers(priv->rx_dmatsk);
+
+ return -EAGAIN;
+}
+
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct bcom_fec_bd *bd;
+
+ if (bcom_queue_full(priv->tx_dmatsk))
+ panic("MPC52xx transmit queue overrun\n");
+
+ spin_lock_irq(&priv->lock);
+ dev->trans_start = jiffies;
+
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->tx_dmatsk);
+
+ bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_INT;
+ bd->skb_pa = virt_to_phys(skb->data);
+
+ bcom_submit_next_buffer(priv->tx_dmatsk, skb);
+
+ if (bcom_queue_full(priv->tx_dmatsk)) {
+ priv->tx_full = 1;
+ netif_stop_queue(dev);
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ return 0;
+}
+
+/* This handles BestComm transmit task interrupts
+ */
+static irqreturn_t fec_tx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ for (;;) {
+ struct sk_buff *skb;
+
+ spin_lock(&priv->lock);
+ if (!bcom_buffer_done(priv->tx_dmatsk)) {
+ spin_unlock(&priv->lock);
+ break;
+ }
+ skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL);
+
+ if (priv->tx_full) {
+ priv->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ spin_unlock(&priv->lock);
+ dev_kfree_skb_irq(skb);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fec_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ for (;;) {
+ struct sk_buff *skb;
+ struct sk_buff *rskb;
+ struct bcom_fec_bd *bd;
+ u32 status;
+
+ if (!bcom_buffer_done(priv->rx_dmatsk))
+ break;
+
+ rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL);
+
+ /* Test for errors in received frame */
+ if (status & 0x370000) {
+ /* Drop packet and reuse the buffer */
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = virt_to_phys(rskb->data);
+
+ bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
+
+ priv->stats.rx_dropped++;
+
+ continue;
+ }
+
+ /* allocate replacement skb */
+ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+ if (skb) {
+ /* Process the received skb */
+ int length = (status & ((1<<11) - 1)) - sizeof(u32);
+ skb_put(rskb, length); /* length included CRC32 */
+
+ rskb->dev = dev;
+ rskb->protocol = eth_type_trans(rskb, dev);
+ netif_rx(rskb);
+ dev->last_rx = jiffies;
+ } else {
+ /* Can't get a new one : reuse the same & drop pkt */
+ printk(KERN_NOTICE
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ priv->stats.rx_dropped++;
+
+ skb = rskb;
+ }
+
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = virt_to_phys(skb->data);
+
+ bcom_submit_next_buffer(priv->rx_dmatsk, skb);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fec_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ int ievent;
+
+ ievent = in_be32(&fec->ievent);
+ if (!ievent)
+ return IRQ_NONE;
+
+ out_be32(&fec->ievent, ievent); /* clear pending events */
+
+ if (ievent & (FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
+ if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
+ printk(KERN_WARNING "FEC_IEVENT_RFIFO_ERROR\n");
+ if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
+ printk(KERN_WARNING "FEC_IEVENT_XFIFO_ERROR\n");
+ fec_reinit(dev);
+ }
+ else if (ievent & FEC_IEVENT_MII)
+ fec_mii(dev);
+ return IRQ_HANDLED;
+}
+
+static int fec_close(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ unsigned long timeout;
+
+ priv->open_time = 0;
+ priv->sequence_done = 0;
+
+ netif_stop_queue(dev);
+
+ bcom_disable(priv->rx_dmatsk); /* disable receive task */
+
+ /* Wait for queues to drain */
+ timeout = jiffies + 2*HZ;
+ while (time_before(jiffies, timeout) &&
+ (!bcom_queue_empty(priv->tx_dmatsk) ||
+ !bcom_queue_empty(priv->rx_dmatsk))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ }
+ if (time_after_eq(jiffies, timeout))
+ printk(KERN_ERR "fec_close: queues didn't drain\n");
+
+ bcom_disable(priv->tx_dmatsk);
+
+ fec_free_rx_buffers(priv->rx_dmatsk);
+
+ fec_get_stats(dev);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *fec_get_stats(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct net_device_stats *stats = &priv->stats;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ stats->rx_bytes = in_be32(&fec->rmon_r_octets);
+ stats->rx_packets = in_be32(&fec->rmon_r_packets);
+ stats->rx_errors = stats->rx_packets - in_be32(&fec->ieee_r_frame_ok);
+ stats->tx_bytes = in_be32(&fec->rmon_t_octets);
+ stats->tx_packets = in_be32(&fec->rmon_t_packets);
+ stats->tx_errors = stats->tx_packets - (
+ in_be32(&fec->ieee_t_frame_ok) +
+ in_be32(&fec->rmon_t_col) +
+ in_be32(&fec->ieee_t_1col) +
+ in_be32(&fec->ieee_t_mcol) +
+ in_be32(&fec->ieee_t_def));
+ stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
+ stats->collisions = in_be32(&fec->rmon_t_col);
+
+ /* detailed rx_errors: */
+ stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
+ + in_be32(&fec->rmon_r_oversize)
+ + in_be32(&fec->rmon_r_frag)
+ + in_be32(&fec->rmon_r_jab);
+ stats->rx_over_errors = in_be32(&fec->r_macerr);
+ stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
+ stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
+ stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
+ stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
+
+ /* detailed tx_errors: */
+ stats->tx_aborted_errors = 0;
+ stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
+ stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
+ stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
+ stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
+
+ return stats;
+}
+
+static void fec_update_stat(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct net_device_stats *stats = &priv->stats;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ out_be32(&fec->mib_control, FEC_MIB_DISABLE);
+ memset_io(&fec->rmon_t_drop, 0,
+ (u32)&fec->reserved10 - (u32)&fec->rmon_t_drop);
+ out_be32(&fec->mib_control, 0);
+ memset(stats, 0, sizeof *stats);
+ fec_get_stats(dev);
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void fec_set_multicast_list(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ u32 rx_control;
+
+ rx_control = in_be32(&fec->r_cntrl);
+
+ if (dev->flags & IFF_PROMISC) {
+ rx_control |= FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, rx_control);
+ } else {
+ rx_control &= ~FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, rx_control);
+
+ if (dev->flags & IFF_ALLMULTI) {
+ out_be32(&fec->gaddr1, 0xffffffff);
+ out_be32(&fec->gaddr2, 0xffffffff);
+ } else {
+ u32 crc;
+ int i;
+ struct dev_mc_list *dmi;
+ u32 gaddr1 = 0x00000000;
+ u32 gaddr2 = 0x00000000;
+
+ dmi = dev->mc_list;
+ for (i=0; i<dev->mc_count; i++) {
+ crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
+ if (crc >= 32)
+ gaddr1 |= 1 << (crc-32);
+ else
+ gaddr2 |= 1 << crc;
+ dmi = dmi->next;
+ }
+ out_be32(&fec->gaddr1, gaddr1);
+ out_be32(&fec->gaddr2, gaddr2);
+ }
+ }
+}
+
+static void __init fec_str2mac(char *str, unsigned char *mac)
+{
+ int i;
+ u64 val64;
+
+ val64 = simple_strtoull(str, NULL, 16);
+
+ for (i = 0; i < 6; i++)
+ mac[5-i] = val64 >> (i*8);
+}
+
+int __init mpc52xx_fec_mac_setup(char *mac_address)
+{
+ fec_str2mac(mac_address, mpc52xx_fec_mac_addr);
+ return 0;
+}
+
+__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup);
+
+static void fec_hw_init(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ out_be32(&fec->op_pause, 0x00010020);
+ out_be32(&fec->rfifo_cntrl, 0x0f000000);
+ out_be32(&fec->rfifo_alarm, 0x0000030c);
+ out_be32(&fec->tfifo_cntrl, 0x0f000000);
+ out_be32(&fec->tfifo_alarm, 0x00000100);
+ out_be32(&fec->x_wmrk, 0x3); /* xmit fifo watermark = 256 */
+ out_be32(&fec->xmit_fsm, 0x03000000); /* enable crc generation */
+ out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */
+ out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */
+
+ fec_restart(dev, 0); /* always use half duplex mode only */
+ /*
+ * Read MIB counters in order to reset them,
+ * then zero all the stats fields in memory
+ */
+ fec_update_stat(dev);
+}
+
+
+static void fec_reinit(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ netif_stop_queue(dev);
+ out_be32(&fec->imask, 0x0);
+
+ /* Disable the rx and tx tasks. */
+ bcom_disable(priv->rx_dmatsk);
+ bcom_disable(priv->tx_dmatsk);
+
+ /* Stop FEC */
+ out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~0x2);
+
+ /* Restart the DMA tasks */
+ bcom_fec_rx_reset(priv->rx_dmatsk);
+ bcom_fec_tx_reset(priv->tx_dmatsk);
+ fec_hw_init(dev);
+
+ if (priv->sequence_done) { /* redo the fec_open() */
+ fec_free_rx_buffers(priv->rx_dmatsk);
+ fec_open(dev);
+ }
+ return;
+}
+
+
+/* ======================================================================== */
+/* OF Driver */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+{
+ int rv;
+ struct net_device *ndev;
+ struct fec_priv *priv = NULL;
+ struct resource mem;
+
+ phys_addr_t rx_fifo;
+ phys_addr_t tx_fifo;
+
+ /* Reserve FEC control zone */
+ rv = of_address_to_resource(op->node, 0, &mem);
+ if (rv) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Error while parsing device node resource\n" );
+ return rv;
+ }
+ if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) {
+ printk(KERN_ERR DRIVER_NAME
+ " - invalid resource size (%lx != %x), check mpc52xx_devices.c\n",
+ (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec),
+ DRIVER_NAME))
+ return -EBUSY;
+
+ /* Get the ether ndev & it's private zone */
+ ndev = alloc_etherdev(sizeof(struct fec_priv));
+ if (!ndev) {
+ rv = -ENOMEM;
+ goto probe_error;
+ }
+
+ priv = (struct fec_priv *)ndev->priv;
+
+ /* Init ether ndev with what we have */
+ ndev->open = fec_open;
+ ndev->stop = fec_close;
+ ndev->hard_start_xmit = fec_hard_start_xmit;
+ ndev->do_ioctl = fec_ioctl;
+ ndev->get_stats = fec_get_stats;
+ ndev->set_mac_address = fec_set_mac_address;
+ ndev->set_multicast_list = fec_set_multicast_list;
+ ndev->tx_timeout = fec_tx_timeout;
+ ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT;
+ ndev->flags &= ~IFF_RUNNING;
+ ndev->base_addr = mem.start;
+
+ priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */
+
+ spin_lock_init(&priv->lock);
+
+ /* ioremap the zones */
+ priv->fec = (struct mpc52xx_fec *)
+ ioremap(mem.start, sizeof(struct mpc52xx_fec));
+
+ if (!priv->fec) {
+ rv = -ENOMEM;
+ goto probe_error;
+ }
+
+ /* Bestcomm init */
+ rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data);
+ tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data);
+
+ priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE);
+ priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
+
+ if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Can not init SDMA tasks\n" );
+ rv = -ENOMEM;
+ goto probe_error;
+ }
+
+ /* Get the IRQ we need one by one */
+ /* Control */
+ ndev->irq = irq_of_parse_and_map(op->node, 0);
+ if (request_irq(ndev->irq, &fec_interrupt, SA_INTERRUPT,
+ DRIVER_NAME "_ctrl", ndev)) {
+ printk(KERN_ERR DRIVER_NAME ": ctrl interrupt request failed\n");
+ rv = -EBUSY;
+ ndev->irq = NO_IRQ; /* Don't try to free it */
+ goto probe_error;
+ }
+
+ /* RX */
+ priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk);
+ if (request_irq(priv->r_irq, &fec_rx_interrupt, SA_INTERRUPT,
+ DRIVER_NAME "_rx", ndev)) {
+ printk(KERN_ERR DRIVER_NAME ": rx interrupt request failed\n");
+ rv = -EBUSY;
+ priv->r_irq = NO_IRQ; /* Don't try to free it */
+ goto probe_error;
+ }
+
+ /* TX */
+ priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk);
+ if (request_irq(priv->t_irq, &fec_tx_interrupt, SA_INTERRUPT,
+ DRIVER_NAME "_tx", ndev)) {
+ printk(KERN_ERR DRIVER_NAME ": tx interrupt request failed\n");
+ rv = -EBUSY;
+ priv->t_irq = NO_IRQ; /* Don't try to free it */
+ goto probe_error;
+ }
+
+ /* MAC address init */
+ if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0)
+ memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6);
+ else
+ fec_get_paddr(ndev, ndev->dev_addr);
+
+ /* Phy speed */
+ priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
+
+ /* Hardware init */
+ fec_hw_init(ndev);
+
+ /* Register the new network device */
+ rv = register_netdev(ndev);
+ if(rv < 0)
+ goto probe_error;
+
+ /* MII init : After register ???? */
+ fec_mii_init(ndev);
+
+ /* We're done ! */
+ dev_set_drvdata(&op->dev, ndev);
+
+ return 0;
+
+
+ /* Error handling - free everything that might be allocated */
+probe_error:
+
+ if (ndev) {
+ if (priv->rx_dmatsk) bcom_fec_rx_release(priv->rx_dmatsk);
+ if (priv->tx_dmatsk) bcom_fec_tx_release(priv->tx_dmatsk);
+
+ if (ndev->irq != NO_IRQ) free_irq(ndev->irq, ndev);
+ if (priv->r_irq != NO_IRQ) free_irq(priv->r_irq, ndev);
+ if (priv->t_irq != NO_IRQ) free_irq(priv->t_irq, ndev);
+
+ if (priv->fec) iounmap(priv->fec);
+
+ free_netdev(ndev);
+ }
+
+ release_mem_region(mem.start, sizeof(struct mpc52xx_fec));
+
+ return rv;
+}
+
+static int
+mpc52xx_fec_remove(struct of_device *op)
+{
+ struct net_device *ndev;
+ struct fec_priv *priv;
+
+ ndev = (struct net_device *) dev_get_drvdata(&op->dev);
+ if (!ndev)
+ return 0;
+ priv = (struct fec_priv *) ndev->priv;
+
+ unregister_netdev(ndev);
+
+ free_irq(ndev->irq, ndev); /* FIXME : unmap irq ??? */
+ free_irq(priv->r_irq, ndev);
+ free_irq(priv->t_irq, ndev);
+
+ /* FIXME release dma tasks ??? */
+
+ iounmap(priv->fec);
+
+ release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec));
+
+ free_netdev(ndev);
+
+ dev_set_drvdata(&op->dev, NULL);
+ return 0;
+}
+
+static struct of_device_id mpc52xx_fec_match[] = {
+ {
+ .type = "network",
+ .compatible = "mpc5200-fec",
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, mpc52xx_fec_match);
+
+static struct of_platform_driver mpc52xx_fec_driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .match_table = mpc52xx_fec_match,
+ .probe = mpc52xx_fec_probe,
+ .remove = mpc52xx_fec_remove,
+#ifdef CONFIG_PM
+/* .suspend = mpc52xx_fec_of_suspend, TODO */
+/* .resume = mpc52xx_fec_of_resume, TODO */
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+
+/* ======================================================================== */
+/* Module */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_fec_init(void)
+{
+ return of_register_platform_driver(&mpc52xx_fec_driver);
+}
+
+static void __exit
+mpc52xx_fec_exit(void)
+{
+ of_unregister_platform_driver(&mpc52xx_fec_driver);
+}
+
+
+module_init(mpc52xx_fec_init);
+module_exit(mpc52xx_fec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC");
+
diff --git a/drivers/net/fec_mpc52xx/fec.h b/drivers/net/fec_mpc52xx/fec.h
new file mode 100644
index 0000000..ef80b0b
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec.h
@@ -0,0 +1,306 @@
+/*
+ * drivers/net/fec_mpc52xx/fec.h
+ *
+ * Driver for the MPC5200 Fast Ethernet 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.
+ */
+
+#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
+#define __DRIVERS_NET_MPC52XX_FEC_H__
+
+/* Tunable constant */
+/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
+#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */
+#define FEC_RX_NUM_BD 64
+#define FEC_TX_NUM_BD 64
+
+#define FEC_RESET_DELAY 50 /* uS */
+
+#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000)
+
+struct fec_priv {
+ int full_duplex;
+ int tx_full;
+ int r_irq;
+ int t_irq;
+ u32 last_transmit_time;
+ struct mpc52xx_fec *fec;
+ struct bcom_task *rx_dmatsk;
+ struct bcom_task *tx_dmatsk;
+ spinlock_t lock;
+ unsigned long open_time;
+ struct net_device_stats stats;
+#ifdef CONFIG_USE_MDIO
+ uint phy_id;
+ uint phy_id_done;
+ uint phy_status;
+ uint phy_speed;
+ phy_info_t *phy;
+ struct tasklet_struct phy_task;
+ uint sequence_done;
+ uint phy_addr;
+ struct timer_list phy_timer_list;
+ u16 old_status;
+#endif /* CONFIG_USE_MDIO */
+};
+
+
+/* ======================================================================== */
+/* Hardware register sets & bits */
+/* ======================================================================== */
+
+struct mpc52xx_fec {
+ u32 fec_id; /* FEC + 0x000 */
+ u32 ievent; /* FEC + 0x004 */
+ u32 imask; /* FEC + 0x008 */
+
+ u32 reserved0[1]; /* FEC + 0x00C */
+ u32 r_des_active; /* FEC + 0x010 */
+ u32 x_des_active; /* FEC + 0x014 */
+ u32 r_des_active_cl; /* FEC + 0x018 */
+ u32 x_des_active_cl; /* FEC + 0x01C */
+ u32 ivent_set; /* FEC + 0x020 */
+ u32 ecntrl; /* FEC + 0x024 */
+
+ u32 reserved1[6]; /* FEC + 0x028-03C */
+ u32 mii_data; /* FEC + 0x040 */
+ u32 mii_speed; /* FEC + 0x044 */
+ u32 mii_status; /* FEC + 0x048 */
+
+ u32 reserved2[5]; /* FEC + 0x04C-05C */
+ u32 mib_data; /* FEC + 0x060 */
+ u32 mib_control; /* FEC + 0x064 */
+
+ u32 reserved3[6]; /* FEC + 0x068-7C */
+ u32 r_activate; /* FEC + 0x080 */
+ u32 r_cntrl; /* FEC + 0x084 */
+ u32 r_hash; /* FEC + 0x088 */
+ u32 r_data; /* FEC + 0x08C */
+ u32 ar_done; /* FEC + 0x090 */
+ u32 r_test; /* FEC + 0x094 */
+ u32 r_mib; /* FEC + 0x098 */
+ u32 r_da_low; /* FEC + 0x09C */
+ u32 r_da_high; /* FEC + 0x0A0 */
+
+ u32 reserved4[7]; /* FEC + 0x0A4-0BC */
+ u32 x_activate; /* FEC + 0x0C0 */
+ u32 x_cntrl; /* FEC + 0x0C4 */
+ u32 backoff; /* FEC + 0x0C8 */
+ u32 x_data; /* FEC + 0x0CC */
+ u32 x_status; /* FEC + 0x0D0 */
+ u32 x_mib; /* FEC + 0x0D4 */
+ u32 x_test; /* FEC + 0x0D8 */
+ u32 fdxfc_da1; /* FEC + 0x0DC */
+ u32 fdxfc_da2; /* FEC + 0x0E0 */
+ u32 paddr1; /* FEC + 0x0E4 */
+ u32 paddr2; /* FEC + 0x0E8 */
+ u32 op_pause; /* FEC + 0x0EC */
+
+ u32 reserved5[4]; /* FEC + 0x0F0-0FC */
+ u32 instr_reg; /* FEC + 0x100 */
+ u32 context_reg; /* FEC + 0x104 */
+ u32 test_cntrl; /* FEC + 0x108 */
+ u32 acc_reg; /* FEC + 0x10C */
+ u32 ones; /* FEC + 0x110 */
+ u32 zeros; /* FEC + 0x114 */
+ u32 iaddr1; /* FEC + 0x118 */
+ u32 iaddr2; /* FEC + 0x11C */
+ u32 gaddr1; /* FEC + 0x120 */
+ u32 gaddr2; /* FEC + 0x124 */
+ u32 random; /* FEC + 0x128 */
+ u32 rand1; /* FEC + 0x12C */
+ u32 tmp; /* FEC + 0x130 */
+
+ u32 reserved6[3]; /* FEC + 0x134-13C */
+ u32 fifo_id; /* FEC + 0x140 */
+ u32 x_wmrk; /* FEC + 0x144 */
+ u32 fcntrl; /* FEC + 0x148 */
+ u32 r_bound; /* FEC + 0x14C */
+ u32 r_fstart; /* FEC + 0x150 */
+ u32 r_count; /* FEC + 0x154 */
+ u32 r_lag; /* FEC + 0x158 */
+ u32 r_read; /* FEC + 0x15C */
+ u32 r_write; /* FEC + 0x160 */
+ u32 x_count; /* FEC + 0x164 */
+ u32 x_lag; /* FEC + 0x168 */
+ u32 x_retry; /* FEC + 0x16C */
+ u32 x_write; /* FEC + 0x170 */
+ u32 x_read; /* FEC + 0x174 */
+
+ u32 reserved7[2]; /* FEC + 0x178-17C */
+ u32 fm_cntrl; /* FEC + 0x180 */
+ u32 rfifo_data; /* FEC + 0x184 */
+ u32 rfifo_status; /* FEC + 0x188 */
+ u32 rfifo_cntrl; /* FEC + 0x18C */
+ u32 rfifo_lrf_ptr; /* FEC + 0x190 */
+ u32 rfifo_lwf_ptr; /* FEC + 0x194 */
+ u32 rfifo_alarm; /* FEC + 0x198 */
+ u32 rfifo_rdptr; /* FEC + 0x19C */
+ u32 rfifo_wrptr; /* FEC + 0x1A0 */
+ u32 tfifo_data; /* FEC + 0x1A4 */
+ u32 tfifo_status; /* FEC + 0x1A8 */
+ u32 tfifo_cntrl; /* FEC + 0x1AC */
+ u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */
+ u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */
+ u32 tfifo_alarm; /* FEC + 0x1B8 */
+ u32 tfifo_rdptr; /* FEC + 0x1BC */
+ u32 tfifo_wrptr; /* FEC + 0x1C0 */
+
+ u32 reset_cntrl; /* FEC + 0x1C4 */
+ u32 xmit_fsm; /* FEC + 0x1C8 */
+
+ u32 reserved8[3]; /* FEC + 0x1CC-1D4 */
+ u32 rdes_data0; /* FEC + 0x1D8 */
+ u32 rdes_data1; /* FEC + 0x1DC */
+ u32 r_length; /* FEC + 0x1E0 */
+ u32 x_length; /* FEC + 0x1E4 */
+ u32 x_addr; /* FEC + 0x1E8 */
+ u32 cdes_data; /* FEC + 0x1EC */
+ u32 status; /* FEC + 0x1F0 */
+ u32 dma_control; /* FEC + 0x1F4 */
+ u32 des_cmnd; /* FEC + 0x1F8 */
+ u32 data; /* FEC + 0x1FC */
+
+ u32 rmon_t_drop; /* FEC + 0x200 */
+ u32 rmon_t_packets; /* FEC + 0x204 */
+ u32 rmon_t_bc_pkt; /* FEC + 0x208 */
+ u32 rmon_t_mc_pkt; /* FEC + 0x20C */
+ u32 rmon_t_crc_align; /* FEC + 0x210 */
+ u32 rmon_t_undersize; /* FEC + 0x214 */
+ u32 rmon_t_oversize; /* FEC + 0x218 */
+ u32 rmon_t_frag; /* FEC + 0x21C */
+ u32 rmon_t_jab; /* FEC + 0x220 */
+ u32 rmon_t_col; /* FEC + 0x224 */
+ u32 rmon_t_p64; /* FEC + 0x228 */
+ u32 rmon_t_p65to127; /* FEC + 0x22C */
+ u32 rmon_t_p128to255; /* FEC + 0x230 */
+ u32 rmon_t_p256to511; /* FEC + 0x234 */
+ u32 rmon_t_p512to1023; /* FEC + 0x238 */
+ u32 rmon_t_p1024to2047; /* FEC + 0x23C */
+ u32 rmon_t_p_gte2048; /* FEC + 0x240 */
+ u32 rmon_t_octets; /* FEC + 0x244 */
+ u32 ieee_t_drop; /* FEC + 0x248 */
+ u32 ieee_t_frame_ok; /* FEC + 0x24C */
+ u32 ieee_t_1col; /* FEC + 0x250 */
+ u32 ieee_t_mcol; /* FEC + 0x254 */
+ u32 ieee_t_def; /* FEC + 0x258 */
+ u32 ieee_t_lcol; /* FEC + 0x25C */
+ u32 ieee_t_excol; /* FEC + 0x260 */
+ u32 ieee_t_macerr; /* FEC + 0x264 */
+ u32 ieee_t_cserr; /* FEC + 0x268 */
+ u32 ieee_t_sqe; /* FEC + 0x26C */
+ u32 t_fdxfc; /* FEC + 0x270 */
+ u32 ieee_t_octets_ok; /* FEC + 0x274 */
+
+ u32 reserved9[2]; /* FEC + 0x278-27C */
+ u32 rmon_r_drop; /* FEC + 0x280 */
+ u32 rmon_r_packets; /* FEC + 0x284 */
+ u32 rmon_r_bc_pkt; /* FEC + 0x288 */
+ u32 rmon_r_mc_pkt; /* FEC + 0x28C */
+ u32 rmon_r_crc_align; /* FEC + 0x290 */
+ u32 rmon_r_undersize; /* FEC + 0x294 */
+ u32 rmon_r_oversize; /* FEC + 0x298 */
+ u32 rmon_r_frag; /* FEC + 0x29C */
+ u32 rmon_r_jab; /* FEC + 0x2A0 */
+
+ u32 rmon_r_resvd_0; /* FEC + 0x2A4 */
+
+ u32 rmon_r_p64; /* FEC + 0x2A8 */
+ u32 rmon_r_p65to127; /* FEC + 0x2AC */
+ u32 rmon_r_p128to255; /* FEC + 0x2B0 */
+ u32 rmon_r_p256to511; /* FEC + 0x2B4 */
+ u32 rmon_r_p512to1023; /* FEC + 0x2B8 */
+ u32 rmon_r_p1024to2047; /* FEC + 0x2BC */
+ u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */
+ u32 rmon_r_octets; /* FEC + 0x2C4 */
+ u32 ieee_r_drop; /* FEC + 0x2C8 */
+ u32 ieee_r_frame_ok; /* FEC + 0x2CC */
+ u32 ieee_r_crc; /* FEC + 0x2D0 */
+ u32 ieee_r_align; /* FEC + 0x2D4 */
+ u32 r_macerr; /* FEC + 0x2D8 */
+ u32 r_fdxfc; /* FEC + 0x2DC */
+ u32 ieee_r_octets_ok; /* FEC + 0x2E0 */
+
+ u32 reserved10[7]; /* FEC + 0x2E4-2FC */
+
+ u32 reserved11[64]; /* FEC + 0x300-3FF */
+};
+
+#define FEC_MIB_DISABLE 0x80000000
+
+#define FEC_IEVENT_HBERR 0x80000000
+#define FEC_IEVENT_BABR 0x40000000
+#define FEC_IEVENT_BABT 0x20000000
+#define FEC_IEVENT_GRA 0x10000000
+#define FEC_IEVENT_TFINT 0x08000000
+#define FEC_IEVENT_MII 0x00800000
+#define FEC_IEVENT_LATE_COL 0x00200000
+#define FEC_IEVENT_COL_RETRY_LIM 0x00100000
+#define FEC_IEVENT_XFIFO_UN 0x00080000
+#define FEC_IEVENT_XFIFO_ERROR 0x00040000
+#define FEC_IEVENT_RFIFO_ERROR 0x00020000
+
+#define FEC_IMASK_HBERR 0x80000000
+#define FEC_IMASK_BABR 0x40000000
+#define FEC_IMASK_BABT 0x20000000
+#define FEC_IMASK_GRA 0x10000000
+#define FEC_IMASK_MII 0x00800000
+#define FEC_IMASK_LATE_COL 0x00200000
+#define FEC_IMASK_COL_RETRY_LIM 0x00100000
+#define FEC_IMASK_XFIFO_UN 0x00080000
+#define FEC_IMASK_XFIFO_ERROR 0x00040000
+#define FEC_IMASK_RFIFO_ERROR 0x00020000
+
+#define FEC_RCNTRL_MAX_FL_SHIFT 16
+#define FEC_RCNTRL_LOOP 0x01
+#define FEC_RCNTRL_DRT 0x02
+#define FEC_RCNTRL_MII_MODE 0x04
+#define FEC_RCNTRL_PROM 0x08
+#define FEC_RCNTRL_BC_REJ 0x10
+#define FEC_RCNTRL_FCE 0x20
+
+#define FEC_TCNTRL_GTS 0x00000001
+#define FEC_TCNTRL_HBC 0x00000002
+#define FEC_TCNTRL_FDEN 0x00000004
+#define FEC_TCNTRL_TFC_PAUSE 0x00000008
+#define FEC_TCNTRL_RFC_PAUSE 0x00000010
+
+#define FEC_ECNTRL_RESET 0x00000001
+#define FEC_ECNTRL_ETHER_EN 0x00000002
+
+struct mibCounters {
+ unsigned int byteReceived;
+ unsigned int byteSent;
+ unsigned int framesReceived;
+ unsigned int framesSent;
+ unsigned int totalByteReceived;
+ unsigned int totalFramesReceived;
+ unsigned int broadcastFramesReceived;
+ unsigned int multicastFramesReceived;
+ unsigned int cRCError;
+ unsigned int oversizeFrames;
+ unsigned int fragments;
+ unsigned int jabber;
+ unsigned int collision;
+ unsigned int lateCollision;
+ unsigned int frames64;
+ unsigned int frames65_127;
+ unsigned int frames128_255;
+ unsigned int frames256_511;
+ unsigned int frames512_1023;
+ unsigned int frames1024_MaxSize;
+ unsigned int macRxError;
+ unsigned int droppedFrames;
+ unsigned int outMulticastFrames;
+ unsigned int outBroadcastFrames;
+ unsigned int undersizeFrames;
+};
+
+
+#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */
diff --git a/drivers/net/fec_mpc52xx/fec_phy.c b/drivers/net/fec_mpc52xx/fec_phy.c
new file mode 100644
index 0000000..a032a81
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec_phy.c
@@ -0,0 +1,526 @@
+/*
+ * arch/ppc/52xx_io/fec_phy.c
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ * Based heavily on the MII support for the MPC8xx by Dan Malek
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/fec.h>
+#include "fec_phy.h"
+#include "fec.h"
+
+static int mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr);
+
+/* MII processing. We keep this as simple as possible. Requests are
+ * placed on the list (if there is room). When the request is finished
+ * by the MII, an optional function may be called.
+ */
+typedef struct mii_list {
+ uint mii_regval;
+ void (*mii_func)(uint val, struct net_device *dev, uint data);
+ struct mii_list *mii_next;
+ uint mii_data;
+} mii_list_t;
+
+#define NMII 20
+mii_list_t mii_cmds[NMII];
+mii_list_t *mii_free;
+mii_list_t *mii_head;
+mii_list_t *mii_tail;
+
+typedef struct mdio_read_data {
+ __u16 regval;
+ struct task_struct *sleeping_task;
+} mdio_read_data_t;
+
+static int mii_queue(struct net_device *dev, int request,
+ void (*func)(uint, struct net_device *, uint), uint data);
+
+/* Make MII read/write commands for the FEC.
+ * */
+#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
+ (VAL & 0xffff))
+#define mk_mii_end 0
+
+/* Register definitions for the PHY.
+*/
+
+#define MII_REG_CR 0 /* Control Register */
+#define MII_REG_SR 1 /* Status Register */
+#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */
+#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */
+#define MII_REG_ANAR 4 /* A-N Advertisement Register */
+#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */
+#define MII_REG_ANER 6 /* A-N Expansion Register */
+#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */
+#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */
+
+/* values for phy_status */
+
+#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */
+#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */
+#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */
+#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */
+#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */
+#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */
+#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */
+
+#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */
+#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */
+#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */
+#define PHY_STAT_SPMASK 0xf000 /* mask for speed */
+#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */
+#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */
+#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */
+#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */
+
+void fec_mii(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ mii_list_t *mip;
+ uint mii_reg;
+
+ mii_reg = in_be32(&fec->mii_data);
+
+ if ((mip = mii_head) == NULL) {
+ printk(KERN_ERR "MII and no head!\n");
+ return;
+ }
+
+ if (mip->mii_func != NULL)
+ (*(mip->mii_func))(mii_reg, dev, mip->mii_data);
+
+ mii_head = mip->mii_next;
+ mip->mii_next = mii_free;
+ mii_free = mip;
+
+ if ((mip = mii_head) != NULL)
+ out_be32(&fec->mii_data, mip->mii_regval);
+}
+
+static int mii_queue(struct net_device *dev, int regval,
+ void (*func)(uint, struct net_device *, uint),
+ uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ mii_list_t *mip;
+ int retval;
+
+ /* Add PHY address to register command.
+ */
+ regval |= priv->phy_addr << 23;
+
+ retval = 0;
+
+ if ((mip = mii_free) != NULL) {
+ mii_free = mip->mii_next;
+ mip->mii_regval = regval;
+ mip->mii_func = func;
+ mip->mii_next = NULL;
+ mip->mii_data = data;
+ if (mii_head) {
+ mii_tail->mii_next = mip;
+ mii_tail = mip;
+ } else {
+ mii_head = mii_tail = mip;
+ out_be32(&fec->mii_data, regval);
+ }
+ } else
+ retval = 1;
+
+ return retval;
+}
+
+static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
+{
+ int k;
+
+ if (!c)
+ return;
+
+ for (k = 0; (c+k)->mii_data != mk_mii_end; k++)
+ mii_queue(dev, (c+k)->mii_data, (c+k)->funct, 0);
+}
+
+static void mii_parse_sr(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
+
+ if (mii_reg & 0x0004)
+ s |= PHY_STAT_LINK;
+ if (mii_reg & 0x0010)
+ s |= PHY_STAT_FAULT;
+ if (mii_reg & 0x0020)
+ s |= PHY_STAT_ANC;
+
+ priv->phy_status = s;
+}
+
+static void mii_parse_cr(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP);
+
+ if (mii_reg & 0x1000)
+ s |= PHY_CONF_ANE;
+ if (mii_reg & 0x4000)
+ s |= PHY_CONF_LOOP;
+
+ priv->phy_status = s;
+}
+
+static void mii_parse_anar(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_CONF_SPMASK);
+
+ if (mii_reg & 0x0020)
+ s |= PHY_CONF_10HDX;
+ if (mii_reg & 0x0040)
+ s |= PHY_CONF_10FDX;
+ if (mii_reg & 0x0080)
+ s |= PHY_CONF_100HDX;
+ if (mii_reg & 0x0100)
+ s |= PHY_CONF_100FDX;
+
+ priv->phy_status = s;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Generic PHY support. Should work for all PHYs, but does not support link
+ * change interrupts.
+ */
+static phy_info_t phy_info_generic = {
+ 0x00000000, /* 0-->match any PHY */
+ "GENERIC",
+
+ (const phy_cmd_t []) { /* config */
+ /* advertise only half-duplex capabilities */
+ { mk_mii_write(MII_ADVERTISE, MII_ADVERTISE_HALF),
+ mii_parse_anar },
+
+ /* enable auto-negotiation */
+ { mk_mii_write(MII_BMCR, BMCR_ANENABLE), mii_parse_cr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ /* restart auto-negotiation */
+ { mk_mii_write(MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)),
+ NULL },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* We don't actually use the ack_int table with a generic
+ * PHY, but putting a reference to mii_parse_sr here keeps
+ * us from getting a compiler warning about unused static
+ * functions in the case where we only compile in generic
+ * PHY support.
+ */
+ { mk_mii_read(MII_BMSR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+/* -------------------------------------------------------------------- */
+
+/* register definitions for the 971 */
+
+#define MII_LXT971_PCR 16 /* Port Control Register */
+#define MII_LXT971_SR2 17 /* Status Register 2 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+#define MII_LXT971_LCR 20 /* LED Control Register */
+#define MII_LXT971_TCR 30 /* Transmit Control Register */
+
+static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_STAT_SPMASK);
+
+ if (mii_reg & 0x4000) {
+ if (mii_reg & 0x0200)
+ s |= PHY_STAT_100FDX;
+ else
+ s |= PHY_STAT_100HDX;
+ } else {
+ if (mii_reg & 0x0200)
+ s |= PHY_STAT_10FDX;
+ else
+ s |= PHY_STAT_10HDX;
+ }
+ if (mii_reg & 0x0008)
+ s |= PHY_STAT_FAULT;
+
+ priv->phy_status = s;
+}
+
+static phy_info_t phy_info_lxt971 = {
+ 0x0001378e,
+ "LXT971",
+
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_write(MII_REG_ANAR, 0x0A1), NULL }, /* 10/100, HD */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+
+ /* Somehow does the 971 tell me that the link is down
+ * the first read after power-up.
+ * read here to get a valid value in ack_int */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* find out the current status */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
+
+ /* we only need to read ISR to acknowledge */
+
+ { mk_mii_read(MII_LXT971_ISR), NULL },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
+ { mk_mii_end, }
+ },
+};
+
+static phy_info_t *phy_info[] = {
+ &phy_info_lxt971,
+ /* Generic PHY support. This must be the last PHY in the table.
+ * It will be used to support any PHY that doesn't match a previous
+ * entry in the table.
+ */
+ &phy_info_generic,
+ NULL
+};
+
+static void mii_display_config(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ printk(KERN_INFO "%s: config: auto-negotiation ", dev->name);
+
+ if (s & PHY_CONF_ANE)
+ printk("on");
+ else
+ printk("off");
+
+ if (s & PHY_CONF_100FDX)
+ printk(", 100FDX");
+ if (s & PHY_CONF_100HDX)
+ printk(", 100HDX");
+ if (s & PHY_CONF_10FDX)
+ printk(", 10FDX");
+ if (s & PHY_CONF_10HDX)
+ printk(", 10HDX");
+ if (!(s & PHY_CONF_SPMASK))
+ printk(", No speed/duplex selected?");
+
+ if (s & PHY_CONF_LOOP)
+ printk(", loopback enabled");
+
+ printk(".\n");
+
+ priv->sequence_done = 1;
+}
+
+static void mii_queue_config(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ priv->phy_task.func = (void *)mii_display_config;
+ priv->phy_task.data = (unsigned long)dev;
+ tasklet_schedule(&priv->phy_task);
+}
+
+
+phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config },
+ { mk_mii_end, } };
+
+
+/* Read remainder of PHY ID.
+*/
+static void mii_discover_phy3(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ int i;
+
+ priv->phy_id |= (mii_reg & 0xffff);
+
+ for (i = 0; phy_info[i]; i++) {
+ if (phy_info[i]->id == (priv->phy_id >> 4) || !phy_info[i]->id)
+ break;
+ if (phy_info[i]->id == 0) /* check generic entry */
+ break;
+ }
+
+ if (!phy_info[i])
+ panic("%s: PHY id 0x%08x is not supported!\n",
+ dev->name, priv->phy_id);
+
+ priv->phy = phy_info[i];
+ priv->phy_id_done = 1;
+
+ printk(KERN_INFO "%s: Phy @ 0x%x, type %s (0x%08x)\n",
+ dev->name, priv->phy_addr, priv->phy->name, priv->phy_id);
+}
+
+/* Scan all of the MII PHY addresses looking for someone to respond
+ * with a valid ID. This usually happens quickly.
+ */
+static void mii_discover_phy(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ uint phytype;
+
+ if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
+ /* Got first part of ID, now get remainder.
+ */
+ priv->phy_id = phytype << 16;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3,
+ 0);
+ } else {
+ priv->phy_addr++;
+ if (priv->phy_addr < 32)
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
+ mii_discover_phy, 0);
+ else
+ printk(KERN_ERR "fec: No PHY device found.\n");
+ }
+}
+
+static int mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+ __u32 ethcmd;
+
+ if (copy_from_user(ðcmd, useraddr, sizeof ethcmd))
+ return -EFAULT;
+
+ switch (ethcmd) {
+
+ /* Get driver info */
+ case ETHTOOL_GDRVINFO:{
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strncpy(info.driver, "MPC5200 FEC",
+ sizeof info.driver - 1);
+ if (copy_to_user(useraddr, &info, sizeof info))
+ return -EFAULT;
+ return 0;
+ }
+ /* get message-level */
+ case ETHTOOL_GMSGLVL:{
+ struct ethtool_value edata = { ETHTOOL_GMSGLVL };
+ edata.data = 0; /* XXX */
+ if (copy_to_user(useraddr, &edata, sizeof edata))
+ return -EFAULT;
+ return 0;
+ }
+ /* set message-level */
+ case ETHTOOL_SMSGLVL:{
+ struct ethtool_value edata;
+ if (copy_from_user(&edata, useraddr, sizeof edata))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ int retval;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ retval = mpc52xx_netdev_ethtool_ioctl(
+ dev, (void *) rq->ifr_data);
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+ return retval;
+}
+
+void fec_mii_init(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+ int i;
+
+ for (i=0; i<NMII-1; i++)
+ mii_cmds[i].mii_next = &mii_cmds[i+1];
+ mii_free = mii_cmds;
+
+ /* Queue up command to detect the PHY and initialize the
+ * remainder of the interface.
+ */
+ priv->phy_id_done = 0;
+ priv->phy_addr = 0;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy, 0);
+
+ priv->old_status = 0;
+}
+
+int fec_mii_wait(struct net_device *dev)
+{
+ struct fec_priv *priv = (struct fec_priv *)dev->priv;
+
+ if (!priv->sequence_done) {
+ if (!priv->phy) {
+ printk("KERN_ERR fec_open: PHY not configured\n");
+ return -ENODEV; /* No PHY we understand */
+ }
+
+ mii_do_cmd(dev, priv->phy->config);
+ mii_do_cmd(dev, phy_cmd_config); /* display configuration */
+ while(!priv->sequence_done)
+ schedule();
+
+ mii_do_cmd(dev, priv->phy->startup);
+ }
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("PHY driver for Motorola MPC52xx FEC");
diff --git a/drivers/net/fec_mpc52xx/fec_phy.h b/drivers/net/fec_mpc52xx/fec_phy.h
new file mode 100644
index 0000000..5c23bff
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec_phy.h
@@ -0,0 +1,73 @@
+/*
+ * arch/ppc/52xx_io/fec_phy.h
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ * Based heavily on the MII support for the MPC8xx by Dan Malek
+ *
+ * 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.
+ */
+
+#ifdef CONFIG_USE_MDIO
+#define MII_ADVERTISE_HALF (ADVERTISE_100HALF | ADVERTISE_10HALF | \
+ ADVERTISE_CSMA)
+
+#define MII_ADVERTISE_ALL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ MII_ADVERTISE_HALF)
+#ifdef PHY_INTERRUPT
+#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_ALL
+#else
+#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_HALF
+#endif
+
+#define MII_RCNTL_MODE FEC_RCNTRL_MII_MODE
+#define set_phy_speed(fec, s) out_be32(&fec->mii_speed, s)
+#define FEC_IMASK_ENABLE 0xf0fe0000
+
+typedef struct {
+ uint mii_data;
+ void (*funct)(uint mii_reg, struct net_device *dev, uint data);
+} phy_cmd_t;
+
+typedef struct {
+ uint id;
+ char *name;
+
+ const phy_cmd_t *config;
+ const phy_cmd_t *startup;
+ const phy_cmd_t *ack_int;
+ const phy_cmd_t *shutdown;
+} phy_info_t;
+
+#else
+#define MII_RCNTL_MODE 0
+#define set_phy_speed(fec, s)
+#define FEC_IMASK_ENABLE 0xf07e0000
+#define fec_mii_wait(dev) 0
+#define fec_mii(dev) printk(KERN_WARNING "unexpected FEC_IEVENT_MII\n")
+#define fec_mii_init(dev)
+#endif /* CONFIG_USE_MDIO */
+
+/* MII-related definitions */
+#define FEC_MII_DATA_ST 0x40000000 /* Start frame */
+#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */
+#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */
+#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */
+#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */
+#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */
+#define FEC_MII_DATA_DATAMSK 0x00000fff /* PHY data mask */
+
+#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */
+#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */
+
+#define FEC_MII_SPEED (5 * 2)
+
+extern void fec_mii_init(struct net_device *dev);
+extern int fec_mii_wait(struct net_device *dev);
+extern void fec_mii(struct net_device *dev);
+
+extern int fec_ioctl(struct net_device *, struct ifreq *rq, int cmd);
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 9/9] sound: Add support for Freescale MPC5200 AC97 interface.
2007-05-12 20:31 ` [PATCH 8/9] drivers/net: Add support for Freescale MPC5200 SoC internal FEC Sylvain Munaut
@ 2007-05-12 20:31 ` Sylvain Munaut
0 siblings, 0 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 20:31 UTC (permalink / raw)
To: Linux PPC dev ML; +Cc: Sylvain Munaut
Not quite a clean driver, but it get things done (well,
mostly).
Only included to be able to test functionalityi/usage of
the BestComm driver.
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
---
sound/ppc/Kconfig | 16 +
sound/ppc/Makefile | 3 +
sound/ppc/mpc52xx_ac97.c | 870 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 889 insertions(+), 0 deletions(-)
create mode 100644 sound/ppc/mpc52xx_ac97.c
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index a3fb149..afd58f7 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -33,3 +33,19 @@ config SND_POWERMAC_AUTO_DRC
option.
endmenu
+
+
+# ALSA ppc drivers
+
+menu "ALSA PPC devices"
+ depends on SND!=n && PPC
+
+config SND_PPC_MPC52xx_AC97
+ tristate "Freescale MPC52xx AC97 interface support"
+ depends on SND && PPC_MPC52xx
+ select SND_AC97_CODEC
+ help
+ Say Y or M if you want to support any AC97 codec attached to
+ the Freescqle MPC52xx AC97 interface.
+
+endmenu
diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile
index 4d95c65..c29cb9b 100644
--- a/sound/ppc/Makefile
+++ b/sound/ppc/Makefile
@@ -4,6 +4,9 @@
#
snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
+snd-mpc52xx-ac97-objs := mpc52xx_ac97.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
+
+obj-$(CONFIG_SND_PPC_MPC52xx_AC97) += snd-mpc52xx-ac97.o
diff --git a/sound/ppc/mpc52xx_ac97.c b/sound/ppc/mpc52xx_ac97.c
new file mode 100644
index 0000000..20e2bf8
--- /dev/null
+++ b/sound/ppc/mpc52xx_ac97.c
@@ -0,0 +1,870 @@
+/*
+ * Driver for the PSC of the Freescale MPC52xx configured as AC97 interface
+ *
+ *
+ * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <asm/mpc52xx_psc.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+
+
+#define DRV_NAME "mpc52xx-psc-ac97"
+
+
+/* ======================================================================== */
+/* Structs / Defines */
+/* ======================================================================== */
+
+/* Private structure */
+struct mpc52xx_ac97_priv {
+ struct device *dev;
+ resource_size_t mem_start;
+ resource_size_t mem_len;
+ int irq;
+ struct mpc52xx_psc __iomem *psc;
+
+ struct bcom_task *tsk_tx;
+ spinlock_t dma_lock;
+
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_ac97 *ac97;
+
+ struct snd_pcm_substream *substream_playback;
+
+ int period_byte_size;
+ u32 period_start, period_end, period_next_p;
+};
+
+/* Register bit definition (AC97 mode specific) */
+#define PSC_AC97_SLOT_BIT(n) (1<<(12-n))
+#define PSC_AC97_SLOTS_XMIT_SHIFT 16
+#define PSC_AC97_SLOTS_RECV_SHIFT 0
+
+/* Bestcomm options */
+#define AC97_TX_NUM_BD 32
+#define AC97_RX_NUM_BD 32
+
+#if 0
+static u32* buffers_va[AC97_TX_NUM_BD];
+static u32 buffers_pa[AC97_TX_NUM_BD];
+#endif
+
+
+/* ======================================================================== */
+/* Buffer handling */
+/* ======================================================================== */
+
+#if 0
+static void
+mpc52xx_ac97_tx_bufalloc(struct mpc52xx_ac97_priv *priv)
+{
+ int i;
+
+ if (buffers_va[0])
+ return;
+
+ buffers_va[0] = dma_alloc_coherent(priv->dev, AC97_TX_NUM_BD * 1024, &buffers_pa[0], GFP_KERNEL);
+
+ for (i=1; i<AC97_TX_NUM_BD; i++) {
+ buffers_va[i] = buffers_va[i-1] + 1024;
+ buffers_pa[i] = buffers_pa[i-1] + 1024;
+ }
+
+ printk(KERN_INFO "Buffer alloc %08x %08x\n", buffers_va[0], buffers_pa[0]);
+}
+
+static void
+mpc52xx_ac97_tx_bufrelease(struct mpc52xx_ac97_priv *priv)
+{
+ if (buffers_va[0]) {
+ dma_free_coherent(priv->dev, AC97_TX_NUM_BD * 1024, buffers_va[0], buffers_pa[0]);
+ buffers_va[0] = NULL;
+ }
+}
+#endif
+
+static int
+mpc52xx_ac97_tx_fill(struct mpc52xx_ac97_priv *priv)
+{
+ struct snd_pcm_runtime *rt;
+
+ u32 dma_data_ptr;
+
+ rt = priv->substream_playback->runtime;
+
+ dma_data_ptr = virt_to_phys(rt->dma_area);
+
+ priv->period_byte_size = frames_to_bytes(rt, rt->period_size);
+ priv->period_start = dma_data_ptr;
+ priv->period_end = dma_data_ptr + priv->period_byte_size * rt->periods;
+ priv->period_next_p = dma_data_ptr;
+
+ spin_lock(&priv->dma_lock);
+ while (!bcom_queue_full(priv->tsk_tx)) {
+ struct bcom_gen_bd *bd;
+
+ /* Submit a new one */
+ bd = (struct bcom_gen_bd *) bcom_prepare_next_buffer(priv->tsk_tx);
+ bd->status = priv->period_byte_size;
+ bd->buf_pa = priv->period_next_p;
+ bcom_submit_next_buffer(priv->tsk_tx, NULL);
+
+ /* Next pointer */
+ priv->period_next_p += priv->period_byte_size;
+ if (priv->period_next_p >= priv->period_end)
+ priv->period_next_p = priv->period_start;
+ }
+ spin_unlock(&priv->dma_lock);
+
+ return 0;
+}
+
+
+/* ======================================================================== */
+/* ISR routine */
+/* ======================================================================== */
+
+static irqreturn_t
+mpc52xx_ac97_tx_irq(int irq, void *dev_id)
+{
+ struct mpc52xx_ac97_priv *priv = dev_id;
+ struct snd_pcm_runtime *rt;
+ struct bcom_gen_bd *bd;
+
+ rt = priv->substream_playback->runtime;
+
+ if (!bcom_buffer_done(priv->tsk_tx)) {
+ bcom_dump_status();
+ bcom_dump_task(priv->tsk_tx->tasknum);
+ bcom_dump_bdring(priv->tsk_tx);
+ bcom_disable(priv->tsk_tx);
+ }
+
+ spin_lock(&priv->dma_lock);
+ while (bcom_buffer_done(priv->tsk_tx)) {
+ /* Get the buffer back */
+ bcom_retrieve_buffer(priv->tsk_tx, NULL, NULL);
+
+ /* Submit a new one */
+ bd = (struct bcom_gen_bd *) bcom_prepare_next_buffer(priv->tsk_tx);
+ bd->status = priv->period_byte_size;
+ bd->buf_pa = priv->period_next_p;
+ bcom_submit_next_buffer(priv->tsk_tx, NULL);
+ bcom_enable(priv->tsk_tx);
+
+ /* Next pointer */
+ priv->period_next_p += priv->period_byte_size;
+ if (priv->period_next_p >= priv->period_end)
+ priv->period_next_p = priv->period_start;
+ }
+ spin_unlock(&priv->dma_lock);
+
+ snd_pcm_period_elapsed(priv->substream_playback);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t
+mpc52xx_ac97_irq(int irq, void *dev_id)
+{
+ struct mpc52xx_ac97_priv *priv = dev_id;
+
+ static int icnt = 0;
+ #if 0
+ {
+ unsigned int val;
+// val = in_be32(&priv->psc->ac97_data);
+ printk(KERN_INFO "mpc52xx_ac97_irq fired (isr=%04x, status=%04x) %08x\n", in_be16(&priv->psc->mpc52xx_psc_imr), in_be16(&priv->psc->mpc52xx_psc_status), val);
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_ERR_STAT);
+ }
+ #endif
+
+ /* Anti Crash during dev ;) */
+ #if 1
+ if ((icnt++) > 5000)
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0);
+ #endif
+
+ /* Print statuts */
+ printk(KERN_DEBUG "isr: %04x", in_be16(&priv->psc->mpc52xx_psc_imr));
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_ERR_STAT);
+
+ return IRQ_HANDLED;
+}
+
+/* ======================================================================== */
+/* PCM interface */
+/* ======================================================================== */
+
+/* HW desc */
+
+static struct snd_pcm_hardware mpc52xx_ac97_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2, /* Support for more ? */
+ .buffer_bytes_max = 1024*1024,
+ .period_bytes_min = 512,
+ .period_bytes_max = 16*1024,
+ .periods_min = 8,
+ .periods_max = 1024,
+ .fifo_size = 512,
+};
+
+
+/* Playback */
+
+static int
+mpc52xx_ac97_playback_open(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_open(%p)\n", substream);
+
+ substream->runtime->hw = mpc52xx_ac97_hw;
+
+ priv->substream_playback = substream;
+
+ return 0; /* FIXME */
+}
+
+static int
+mpc52xx_ac97_playback_close(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_close(%p)\n", substream);
+ priv->substream_playback = NULL;
+ return 0; /* FIXME */
+}
+
+static int
+mpc52xx_ac97_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_prepare(%p)\n", substream);
+
+ /* FIXME, need a spinlock to protect access */
+ if (substream->runtime->channels == 1)
+ out_be32(&priv->psc->ac97_slots, 0x01000000);
+ else
+ out_be32(&priv->psc->ac97_slots, 0x03000000);
+
+ snd_ac97_set_rate(priv->ac97, AC97_PCM_FRONT_DAC_RATE, substream->runtime->rate);
+
+ return 0; /* FIXME */
+}
+
+
+/* Capture */
+
+static int
+mpc52xx_ac97_capture_open(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ return 0; /* FIXME */
+}
+
+static int
+mpc52xx_ac97_capture_close(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ return 0; /* FIXME */
+}
+
+static int
+mpc52xx_ac97_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ return 0; /* FIXME */
+}
+
+
+/* Common */
+
+static int
+mpc52xx_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ int rv;
+
+ dev_dbg(priv->dev, "mpc52xx_ac97_hw_params(%p)\n", substream);
+
+ rv = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(params));
+ if (rv < 0) {
+ printk(KERN_ERR "hw params failes\n"); /* FIXME */
+ return rv;
+ }
+
+ // mpc52xx_ac97_tx_bufalloc(priv);
+
+ printk(KERN_DEBUG "%d %d %d\n", params_buffer_bytes(params), params_period_bytes(params), params_periods(params));
+
+ return 0;
+}
+
+static int
+mpc52xx_ac97_hw_free(struct snd_pcm_substream *substream)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+
+ dev_dbg(priv->dev, "mpc52xx_ac97_hw_free(%p)\n", substream);
+ // mpc52xx_ac97_tx_bufrelease(priv);
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int
+mpc52xx_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ int rv = 0;
+
+ dev_dbg(priv->dev, "mpc52xx_ac97_trigger(%p,%d)\n", substream, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* Enable TX taks */
+ bcom_gen_bd_tx_reset(priv->tsk_tx);
+ mpc52xx_ac97_tx_fill(priv);
+ bcom_enable(priv->tsk_tx);
+// out_be16(&priv->psc->mpc52xx_psc_imr, 0x0800); // 0x0100
+// out_be16(&priv->psc->mpc52xx_psc_imr, 0x0100); // 0x0100
+ /* FIXME: Shouldn't we check for overrun too ? */
+ /* also, shouldn't we just activate TX here ? */
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Disable TX task */
+ bcom_disable(priv->tsk_tx);
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000); // 0x0100
+
+ break;
+
+ default:
+ rv = -EINVAL;
+ }
+
+ /* FIXME */
+ return rv;
+}
+
+static snd_pcm_uframes_t
+mpc52xx_ac97_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
+ u32 count;
+
+// dev_dbg(priv->dev, "mpc52xx_ac97_pointer(%p)\n", substream);
+
+ count = priv->tsk_tx->bd[priv->tsk_tx->outdex].data[0] - priv->period_start;
+
+ return bytes_to_frames(runtime, count);
+}
+
+
+/* Ops */
+
+static struct snd_pcm_ops mpc52xx_ac97_playback_ops = {
+ .open = mpc52xx_ac97_playback_open,
+ .close = mpc52xx_ac97_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = mpc52xx_ac97_hw_params,
+ .hw_free = mpc52xx_ac97_hw_free,
+ .prepare = mpc52xx_ac97_playback_prepare,
+ .trigger = mpc52xx_ac97_trigger,
+ .pointer = mpc52xx_ac97_pointer,
+};
+
+static struct snd_pcm_ops mpc52xx_ac97_capture_ops = {
+ .open = mpc52xx_ac97_capture_open,
+ .close = mpc52xx_ac97_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = mpc52xx_ac97_hw_params,
+ .hw_free = mpc52xx_ac97_hw_free,
+ .prepare = mpc52xx_ac97_capture_prepare,
+ .trigger = mpc52xx_ac97_trigger,
+ .pointer = mpc52xx_ac97_pointer,
+};
+
+
+/* ======================================================================== */
+/* AC97 Bus interface */
+/* ======================================================================== */
+
+static unsigned short
+mpc52xx_ac97_bus_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
+ int timeout;
+ unsigned int val;
+
+// dev_dbg(priv->dev, "ac97 read: reg %04x\n", reg);
+
+ /* Wait for it to be ready */
+ timeout = 1000;
+ while ((--timeout) && (in_be16(&priv->psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_CMDSEND) )
+ udelay(10);
+
+ if (!timeout) {
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 bus (rdy)\n");
+ return 0xffff;
+ }
+
+ /* Do the read */
+ out_be32(&priv->psc->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24));
+
+ /* Wait for the answer */
+ timeout = 1000;
+ while ((--timeout) && !(in_be16(&priv->psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_DATA_VAL) )
+ udelay(10);
+
+ if (!timeout) {
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 read (val)\n");
+ return 0xffff;
+ }
+
+ /* Get the data */
+ val = in_be32(&priv->psc->ac97_data);
+ if ( ((val>>24) & 0x7f) != reg ) {
+ printk(KERN_ERR DRV_NAME ": reg echo error on ac97 read\n");
+ return 0xffff;
+ }
+ val = (val >> 8) & 0xffff;
+
+// dev_dbg(priv->dev, "ac97 read ok: reg %04x val %04x\n",
+// reg, val);
+
+ return (unsigned short) val;
+}
+
+static void
+mpc52xx_ac97_bus_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+{
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
+ int timeout;
+
+// dev_dbg(priv->dev, "ac97 write: reg %04x val %04x\n",
+// reg, val);
+
+ /* Wait for it to be ready */
+ timeout = 1000;
+ while ((--timeout) && (in_be16(&priv->psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_CMDSEND) )
+ udelay(10);
+
+ if (!timeout) {
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 write\n");
+ return;
+ }
+
+ /* Write data */
+ out_be32(&priv->psc->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8));
+}
+
+static void
+mpc52xx_ac97_bus_reset(struct snd_ac97 *ac97)
+{
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
+
+ dev_dbg(priv->dev, "ac97 codec reset\n");
+
+ /* Do a cold reset */
+ out_8(&priv->psc->op1, 0x03);
+ udelay(10);
+ out_8(&priv->psc->op0, 0x02);
+ udelay(50);
+
+ /* PSC recover from cold reset (cfr user manual, not sure if useful) */
+ out_be32(&priv->psc->sicr, in_be32(&priv->psc->sicr));
+}
+
+
+static struct snd_ac97_bus_ops mpc52xx_ac97_bus_ops = {
+ .read = mpc52xx_ac97_bus_read,
+ .write = mpc52xx_ac97_bus_write,
+ .reset = mpc52xx_ac97_bus_reset,
+};
+
+
+/* ======================================================================== */
+/* Sound driver setup */
+/* ======================================================================== */
+
+static int
+mpc52xx_ac97_setup_pcm(struct mpc52xx_ac97_priv *priv)
+{
+ int rv;
+
+ rv = snd_pcm_new(priv->card, DRV_NAME "-pcm", 0, 1, 1, &priv->pcm);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME ": snd_pcm_new failed\n");
+ return rv;
+ }
+
+ rv = snd_pcm_lib_preallocate_pages_for_all(priv->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL),
+ 128*1024, 128*1024);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME
+ ": snd_pcm_lib_preallocate_pages_for_all failed\n");
+ return rv;
+ }
+
+ snd_pcm_set_ops(priv->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &mpc52xx_ac97_playback_ops);
+ snd_pcm_set_ops(priv->pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &mpc52xx_ac97_capture_ops);
+
+ priv->pcm->private_data = priv;
+ priv->pcm->info_flags = 0;
+
+ strcpy(priv->pcm->name, "Freescale MPC52xx PSC-AC97 PCM");
+
+ return 0;
+}
+
+static int
+mpc52xx_ac97_setup_mixer(struct mpc52xx_ac97_priv *priv)
+{
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97_template ac97_template;
+ int rv;
+
+ rv = snd_ac97_bus(priv->card, 0, &mpc52xx_ac97_bus_ops, NULL, &ac97_bus);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME ": snd_ac97_bus failed\n");
+ return rv;
+ }
+
+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
+ ac97_template.private_data = priv;
+
+ rv = snd_ac97_mixer(ac97_bus, &ac97_template, &priv->ac97);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME ": snd_ac97_mixer failed\n");
+ return rv;
+ }
+
+ return 0;
+}
+
+
+static int
+mpc52xx_ac97_hwinit(struct mpc52xx_ac97_priv *priv)
+{
+ /* Reset everything first by safety */
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_RX);
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_TX);
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_ERR_STAT);
+
+ /* Do a cold reset of codec */
+ out_8(&priv->psc->op1, 0x03);
+ udelay(10);
+ out_8(&priv->psc->op0, 0x02);
+ udelay(50);
+
+ /* Configure AC97 enhanced mode */
+ out_be32(&priv->psc->sicr, 0x03010000);
+
+ /* No slots active */
+ out_be32(&priv->psc->ac97_slots, 0x00000000);
+
+ /* No IRQ */
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000);
+
+ /* FIFO levels */
+ out_8(&priv->psc->rfcntl, 0x07);
+ out_8(&priv->psc->tfcntl, 0x07);
+ out_be16(&priv->psc->rfalarm, 0x80);
+ out_be16(&priv->psc->tfalarm, 0x80);
+
+ /* Go */
+ out_8(&priv->psc->command,MPC52xx_PSC_TX_ENABLE);
+ out_8(&priv->psc->command,MPC52xx_PSC_RX_ENABLE);
+
+ return 0;
+}
+
+static int
+mpc52xx_ac97_hwshutdown(struct mpc52xx_ac97_priv *priv)
+{
+ /* No IRQ */
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000);
+
+ /* Disable TB & RX */
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_RX);
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_TX);
+
+ /* FIXME : Reset or put codec in low power ? */
+
+ return 0;
+}
+
+
+/* ======================================================================== */
+/* OF Platform Driver */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_ac97_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct device_node *dn = op->node;
+ struct mpc52xx_ac97_priv *priv;
+ struct snd_card *card;
+ struct resource res;
+ int rv;
+
+ dev_dbg(&op->dev, "probing MPC52xx PSC AC97 driver\n");
+
+ /* Get card structure */
+ rv = -ENOMEM;
+ card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct mpc52xx_ac97_priv));
+ if (!card)
+ goto err_early;
+
+ priv = card->private_data;
+
+ /* Init our private structure */
+ priv->card = card;
+ priv->dev = &op->dev;
+
+ /* Get resources (mem,irq,...) */
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ goto err_early;
+
+ priv->mem_start = res.start;
+ priv->mem_len = res.end - res.start + 1;
+
+ if (!request_mem_region(priv->mem_start, priv->mem_len, DRV_NAME)) {
+ printk(KERN_ERR DRV_NAME ": request_mem_region failed\n");
+ rv = -EBUSY;
+ goto err_early;
+ }
+
+ priv->psc = ioremap(priv->mem_start, priv->mem_len);
+ if (!priv->psc) {
+ printk(KERN_ERR DRV_NAME ": ioremap failed\n");
+ rv = -ENOMEM;
+ goto err_iomap;
+ }
+
+ priv->irq = irq_of_parse_and_map(dn, 0);
+ if (priv->irq == NO_IRQ) {
+ printk(KERN_ERR DRV_NAME ": irq_of_parse_and_map failed\n");
+ rv = -EBUSY;
+ goto err_irqmap;
+ }
+
+ /* Setup Bestcomm tasks */
+ spin_lock_init(&priv->dma_lock);
+
+ priv->tsk_tx = bcom_gen_bd_tx_init(AC97_TX_NUM_BD,
+ priv->mem_start + offsetof(struct mpc52xx_psc,
+ tfdata),
+ 12, /* initiator : FIXME */
+ 2); /* ipr : FIXME */
+ if (!priv->tsk_tx) {
+ printk(KERN_ERR DRV_NAME ": bcom_gen_bd_tx_init failed\n");
+ rv = -ENOMEM;
+ goto err_bcomm;
+ }
+
+ /* Low level HW Init */
+ mpc52xx_ac97_hwinit(priv);
+
+ /* Request IRQ now that we're 'stable' */
+ rv = request_irq(priv->irq, mpc52xx_ac97_irq, 0, DRV_NAME, priv);
+ if (rv < 0) {
+ printk(KERN_ERR DRV_NAME ": request_irq failed\n");
+ goto err_irqreq;
+ }
+
+ rv = request_irq(bcom_get_task_irq(priv->tsk_tx),
+ mpc52xx_ac97_tx_irq, 0, DRV_NAME "_tx", priv);
+ if (rv < 0) {
+ printk(KERN_ERR DRV_NAME ": request_irq failed\n");
+ goto err_txirqreq;
+ }
+
+ /* Prepare sound stuff */
+ rv = mpc52xx_ac97_setup_mixer(priv);
+ if (rv)
+ goto err_late;
+
+ rv = mpc52xx_ac97_setup_pcm(priv);
+ if (rv)
+ goto err_late;
+
+ /* Finally register the card */
+ snprintf(card->shortname, sizeof(card->shortname), DRV_NAME);
+ snprintf(card->longname, sizeof(card->longname),
+ "Freescale MPC52xx PSC-AC97 (%s)", card->mixername);
+
+ rv = snd_card_register(card);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME ": snd_card_register failed\n");
+ goto err_late;
+ }
+
+ dev_set_drvdata(&op->dev, priv);
+
+ return 0;
+
+err_late:
+ free_irq(bcom_get_task_irq(priv->tsk_tx), priv);
+err_txirqreq:
+ free_irq(priv->irq, priv);
+err_irqreq:
+ bcom_gen_bd_tx_release(priv->tsk_tx);
+err_bcomm:
+ mpc52xx_ac97_hwshutdown(priv);
+ irq_dispose_mapping(priv->irq);
+err_irqmap:
+ iounmap(priv->psc);
+err_iomap:
+ release_mem_region(priv->mem_start, priv->mem_len);
+err_early:
+ if (card)
+ snd_card_free(card);
+ return rv;
+}
+
+static int
+mpc52xx_ac97_remove(struct of_device *op)
+{
+ struct mpc52xx_ac97_priv *priv;
+
+ dev_dbg(&op->dev, "removing MPC52xx PSC AC97 driver\n");
+
+ priv = dev_get_drvdata(&op->dev);
+ if (priv) {
+ /* Sound subsys shutdown */
+ snd_card_free(priv->card);
+
+ /* Low level HW shutdown */
+ mpc52xx_ac97_hwshutdown(priv);
+
+ /* Release bestcomm tasks */
+ free_irq(bcom_get_task_irq(priv->tsk_tx), priv);
+ bcom_gen_bd_tx_release(priv->tsk_tx);
+
+ /* Release resources */
+ iounmap(priv->psc);
+ free_irq(priv->irq, priv);
+ irq_dispose_mapping(priv->irq);
+ release_mem_region(priv->mem_start, priv->mem_len);
+ }
+
+ dev_set_drvdata(&op->dev, NULL);
+
+ return 0;
+}
+
+
+static struct of_device_id mpc52xx_ac97_of_match[] = {
+ {
+ .type = "sound",
+ .compatible = "mpc5200b-psc-ac97", /* B only for now */
+ },
+};
+/* Prevent autoload during developpment phase ... */
+/* MODULE_DEVICE_TABLE(of, mpc52xx_ac97_of_match); */
+
+
+static struct of_platform_driver mpc52xx_ac97_of_driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .match_table = mpc52xx_ac97_of_match,
+ .probe = mpc52xx_ac97_probe,
+ .remove = mpc52xx_ac97_remove,
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+
+/* ======================================================================== */
+/* Module */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_ac97_init(void)
+{
+ int rv;
+
+ /* FIXME BIG FAT EFIKA HACK */
+ {
+ void *mbar;
+ mbar = ioremap(0xf0000000, 0x100000);
+ printk(KERN_INFO "EFIKA HACK: port_config %08x\n", in_be32(mbar + 0xb00));
+ out_be32(mbar + 0xb00, 0x01051124);
+ printk(KERN_INFO "EFIKA HACK: port_config %08x\n", in_be32(mbar + 0xb00));
+ iounmap(mbar);
+ }
+ /* ------------------------ */
+
+ printk(KERN_INFO "Sound: MPC52xx PSC AC97 driver\n");
+
+ rv = of_register_platform_driver(&mpc52xx_ac97_of_driver);
+ if (rv) {
+ printk(KERN_ERR DRV_NAME ": "
+ "of_register_platform_driver failed (%i)\n", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+static void __exit
+mpc52xx_ac97_exit(void)
+{
+ of_unregister_platform_driver(&mpc52xx_ac97_of_driver);
+}
+
+module_init(mpc52xx_ac97_init);
+module_exit(mpc52xx_ac97_exit);
+
+MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
+MODULE_DESCRIPTION(DRV_NAME ": Freescale MPC52xx PSC AC97 driver");
+MODULE_LICENSE("GPL");
+
--
1.5.1.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
@ 2007-05-12 23:27 ` Arnd Bergmann
2007-05-12 23:49 ` Sylvain Munaut
2007-05-15 21:37 ` Kumar Gala
2 siblings, 1 reply; 24+ messages in thread
From: Arnd Bergmann @ 2007-05-12 23:27 UTC (permalink / raw)
To: linuxppc-dev
On Saturday 12 May 2007, Sylvain Munaut wrote:
> This patch adds support for the core of the BestComm API
> for the Freescale MPC5200(b). The BestComm engine is a
> microcode-controlled / tasks-based DMA used by several
> of the onchip devices.
Have you tried if the DMA engine API that is now in drivers/dma
fits the hardware interfaces? Could they perhaps be extended to
do so?
> +struct bcom_engine *bcom = NULL;
Doe this need to be a global? In most cases, you get a cleaner driver by
passing around pointers through function calls and your own device specific
data structures.
If you can't make that work, at least give it a longer name to make sure
it doesn't conflict with other global variables.
> +/* ======================================================================== */
> +/* Public and private API */
> +/* ======================================================================== */
> +
> +/* Debug Dump */
> +
> +#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a, ## b)
just use pr_debug in new code instead of defining your own
> +static int __init
> +mpc52xx_bcom_init(void)
> +{
> + struct device_node *ofn_bcom, *ofn_sram;
> + struct resource res_bcom;
> +
> + int rv;
> +
> + /* Find the bestcomm node. If none, fails 'silently' since
> + * we may just be on another platform */
> + ofn_bcom = of_find_compatible_node(
> + NULL, "dma-controller", "mpc5200-bestcomm");
> + if (!ofn_bcom)
> + return -ENODEV;
I know, my usual rant is getting old, but why is this one not an
of_platform_driver? It's not shared with arch/ppc or with arch/mips,
and it's not needed before module_init() time.
> +
> +#ifdef MODULE
> +module_init(mpc52xx_bcom_init);
> +module_exit(mpc52xx_bcom_exit);
> +#endif
> +
> +/* If we're not a module, we must make sure everything is setup before anyone */
> +/* tries to use us ... */
> +#ifndef MODULE
> +subsys_initcall(mpc52xx_bcom_init);
> +#endif
You can make it subsys_initcall() unconditionally. If the driver gets built
as a module, it will turn into module_init() by itself.
> +MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
> +MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
> +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
> +MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
> +MODULE_LICENSE("GPL v2");
I didn't know that you could have multiple MODULE_AUTHOR statements.
Does this do the right thing with /bin/modinfo?
> +EXPORT_SYMBOL(bcom);
> +EXPORT_SYMBOL(bcom_dump_status);
> +EXPORT_SYMBOL(bcom_dump_task);
> +EXPORT_SYMBOL(bcom_dump_bdring);
> +EXPORT_SYMBOL(bcom_task_alloc);
> +EXPORT_SYMBOL(bcom_task_release);
> +EXPORT_SYMBOL(bcom_load_image);
> +EXPORT_SYMBOL(bcom_set_initiator);
> +EXPORT_SYMBOL(bcom_enable);
> +EXPORT_SYMBOL(bcom_disable);
Please put every EXPORT_SYMBOL right after the definition of the symbol
you are exporting.
Why don't you use EXPORT_SYMBOL_GPL?
> +
> +EXPORT_SYMBOL(bcom_sram);
> +
> +EXPORT_SYMBOL(bcom_sram_init);
> +EXPORT_SYMBOL(bcom_sram_cleanup);
> +EXPORT_SYMBOL(bcom_sram_alloc);
> +EXPORT_SYMBOL(bcom_sram_free);
> +
Same comment as above.
Arnd <><
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 5/9] powerpc: BestcComm ATA task support
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
2007-05-12 20:31 ` [PATCH 6/9] powerpc: BestcComm FEC " Sylvain Munaut
@ 2007-05-12 23:30 ` Arnd Bergmann
1 sibling, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2007-05-12 23:30 UTC (permalink / raw)
To: linuxppc-dev
On Saturday 12 May 2007, Sylvain Munaut wrote:
> +
> +EXPORT_SYMBOL(bcom_ata_init);
> +EXPORT_SYMBOL(bcom_ata_rx_prepare);
> +EXPORT_SYMBOL(bcom_ata_tx_prepare);
> +EXPORT_SYMBOL(bcom_ata_reset);
> +EXPORT_SYMBOL(bcom_ata_release);
See my comment in 4/9: why not EXPORT_SYMBOL_GPL?
> +u32 bcom_ata_task[] = {
> + /* header */
> + 0x4243544b,
> + 0x0e060709,
> + 0x00000000,
> + 0x00000000,
> +
> + /* Task descriptors */
> + 0x8198009b, /* LCD: idx0 = var3; idx0 <= var2; idx0 += inc3 */
> + 0x13e00c08, /* DRD1A: var3 = var1; FN=0 MORE init=31 WS=0 RS=0 */
> + 0xb8000264, /* LCD: idx1 = *idx0, idx2 = var0; idx1 < var9; idx1 += inc4, idx2 += inc4 */
> + 0x10000f00, /* DRD1A: var3 = idx0; 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 */
> + 0x0c8cfc8a, /* DRD2B1: *idx2 = EU3(); EU3(*idx2,var10) */
> + 0xd8988240, /* LCDEXT: idx1 = idx1; idx1 > var9; idx1 += inc0 */
> + 0xf845e011, /* LCDEXT: idx2 = *(idx0 + var00000015); ; idx2 += inc2 */
> + 0xb845e00a, /* LCD: idx3 = *(idx0 + var00000019); ; idx3 += inc1 */
> + 0x0bfecf90, /* DRD1A: *idx3 = *idx2; FN=0 TFD init=31 WS=3 RS=3 */
> + 0x9898802d, /* LCD: idx1 = idx1; idx1 once var0; idx1 += inc5 */
> + 0x64000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 INT EXT init=0 WS=0 RS=0 */
> + 0x0c0cf849, /* DRD2B1: *idx0 = EU3(); EU3(idx1,var9) */
> + 0x000001f8, /* NOP */
> +
> + /* VAR[9]-VAR[14] */
> + 0x40000000,
> + 0x7fff7fff,
> + 0x00000000,
> + 0x00000000,
> + 0x00000000,
> + 0x00000000,
> +
> + /* INC[0]-INC[6] */
> + 0x40000000,
> + 0xe0000000,
> + 0xe0000000,
> + 0xa000000c,
> + 0x20000000,
> + 0x00000000,
> + 0x00000000,
> +};
> +
Does it make any sense to define these symbolically? It's entirely nonobvious
what all these magic numbers do. Maybe at least give the URL of the document
that this comes from.
Arnd <><
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 23:27 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Arnd Bergmann
@ 2007-05-12 23:49 ` Sylvain Munaut
2007-05-13 0:24 ` Arnd Bergmann
2007-05-13 3:36 ` Dale Farnsworth
0 siblings, 2 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-12 23:49 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linuxppc-dev
Hi Arnd,
Arnd Bergmann wrote:
> On Saturday 12 May 2007, Sylvain Munaut wrote:
>
>> This patch adds support for the core of the BestComm API
>> for the Freescale MPC5200(b). The BestComm engine is a
>> microcode-controlled / tasks-based DMA used by several
>> of the onchip devices.
>
> Have you tried if the DMA engine API that is now in drivers/dma
> fits the hardware interfaces? Could they perhaps be extended to
> do so?
BestComm has some pretty specific needs and I think trying to
somehow extends a generic interface to fit those needs would just
pollute it.
However, you could totally implement a "DMA devices" that would just
use a simple "copy from there to there" task using this BestComm driver.
So other part of the kernel (like network) could use that interface to
use the dma engine ...
>> +struct bcom_engine *bcom = NULL;
>
> Doe this need to be a global? In most cases, you get a cleaner driver by
> passing around pointers through function calls and your own device specific
> data structures.
>
> If you can't make that work, at least give it a longer name to make sure
> it doesn't conflict with other global variables.
Yes, it kinda needs to be global. It's used in many many places.
I didn't like it much but I finally resigned because it ends up being a
lot easier, and more readable code to have it global.
I can rename it bcom_eng, quite unlikely to conflict.
>> +/* ======================================================================== */
>> +/* Public and private API */
>> +/* ======================================================================== */
>> +
>> +/* Debug Dump */
>> +
>> +#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a, ## b)
>
> just use pr_debug in new code instead of defining your own
>
Noted, will be fixed.
>> +static int __init
>> +mpc52xx_bcom_init(void)
>> +{
>> + struct device_node *ofn_bcom, *ofn_sram;
>> + struct resource res_bcom;
>> +
>> + int rv;
>> +
>> + /* Find the bestcomm node. If none, fails 'silently' since
>> + * we may just be on another platform */
>> + ofn_bcom = of_find_compatible_node(
>> + NULL, "dma-controller", "mpc5200-bestcomm");
>> + if (!ofn_bcom)
>> + return -ENODEV;
>
> I know, my usual rant is getting old, but why is this one not an
> of_platform_driver? It's not shared with arch/ppc or with arch/mips,
> and it's not needed before module_init() time.
It needs to be initialized before _any_ other driver that uses bestcomm.
When compiled as module that could be an of_platform_driver but when
built-in there is apparently no way to ensure it's going to be probed
first. (At least no clean way ... )
>> +
>> +#ifdef MODULE
>> +module_init(mpc52xx_bcom_init);
>> +module_exit(mpc52xx_bcom_exit);
>> +#endif
>> +
>> +/* If we're not a module, we must make sure everything is setup before anyone */
>> +/* tries to use us ... */
>> +#ifndef MODULE
>> +subsys_initcall(mpc52xx_bcom_init);
>> +#endif
>
> You can make it subsys_initcall() unconditionally. If the driver gets built
> as a module, it will turn into module_init() by itself.
Oh, great, I didn't know that. I guess the module_exit still has to be
conditionnal.
>> +MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
>> +MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
>> +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
>> +MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
>> +MODULE_LICENSE("GPL v2");
>
> I didn't know that you could have multiple MODULE_AUTHOR statements.
> Does this do the right thing with /bin/modinfo?
I wasn't sure before I tried it myself. And it worked, so I used it ;)
tnt@efika:~$ /sbin/modinfo bestcomm-core
filename:
/lib/modules/2.6.21-efika-g4f18984a-dirty/kernel/arch/powerpc/sysdev/bestcomm/bestcomm-core.ko
license: GPL v2
author: Sylvain Munaut <tnt@246tNt.com>
author: Andrey Volkov <avolkov@varma-el.com>
author: Dale Farnsworth <dfarnsworth@mvista.com>
description: Freescale MPC52xx BestComm DMA
depends:
vermagic: 2.6.21-efika-g4f18984a-dirty mod_unload
>> +EXPORT_SYMBOL(bcom);
>> +EXPORT_SYMBOL(bcom_dump_status);
>> +EXPORT_SYMBOL(bcom_dump_task);
>> +EXPORT_SYMBOL(bcom_dump_bdring);
>> +EXPORT_SYMBOL(bcom_task_alloc);
>> +EXPORT_SYMBOL(bcom_task_release);
>> +EXPORT_SYMBOL(bcom_load_image);
>> +EXPORT_SYMBOL(bcom_set_initiator);
>> +EXPORT_SYMBOL(bcom_enable);
>> +EXPORT_SYMBOL(bcom_disable);
>
> Please put every EXPORT_SYMBOL right after the definition of the symbol
> you are exporting.
Ok, I've seen both style I didn't know which to use.
> Why don't you use EXPORT_SYMBOL_GPL?
Why would I ? Is it mandatory now ?
I don't really have an objection to non-gpl modules to use the exported
functions ...
If Dale or Andrey have, they're free to post a patch to change to _GPL.
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 23:49 ` Sylvain Munaut
@ 2007-05-13 0:24 ` Arnd Bergmann
2007-05-13 7:17 ` Sylvain Munaut
2007-05-13 3:36 ` Dale Farnsworth
1 sibling, 1 reply; 24+ messages in thread
From: Arnd Bergmann @ 2007-05-13 0:24 UTC (permalink / raw)
To: linuxppc-dev
On Sunday 13 May 2007, Sylvain Munaut wrote:
> BestComm has some pretty specific needs and I think trying to
> somehow extends a generic interface to fit those needs would just
> pollute it.
ok.
> However, you could totally implement a "DMA devices" that would just
> use a simple "copy from there to there" task using this BestComm driver.
> So other part of the kernel (like network) could use that interface to
> use the dma engine ...
Can it also do the advanced operations like XOR on DMA that the
Intel ioat has? That would be even more interesting, I guess.
> >> +static int __init
> >> +mpc52xx_bcom_init(void)
> >> +{
> >> + struct device_node *ofn_bcom, *ofn_sram;
> >> + struct resource res_bcom;
> >> +
> >> + int rv;
> >> +
> >> + /* Find the bestcomm node. If none, fails 'silently' since
> >> + * we may just be on another platform */
> >> + ofn_bcom = of_find_compatible_node(
> >> + NULL, "dma-controller", "mpc5200-bestcomm");
> >> + if (!ofn_bcom)
> >> + return -ENODEV;
> >
> > I know, my usual rant is getting old, but why is this one not an
> > of_platform_driver? It's not shared with arch/ppc or with arch/mips,
> > and it's not needed before module_init() time.
> It needs to be initialized before _any_ other driver that uses bestcomm.
> When compiled as module that could be an of_platform_driver but when
> built-in there is apparently no way to ensure it's going to be probed
> first. (At least no clean way ... )
Initialization order is always a little tricky, but if you use a
subsys_initcall, it should at least come before any regular driver
like network or disk, and arch/ drivers come before any code in drivers/,
so I don't see much of a problem here.
> >> +
> >> +#ifdef MODULE
> >> +module_init(mpc52xx_bcom_init);
> >> +module_exit(mpc52xx_bcom_exit);
> >> +#endif
> >> +
> >> +/* If we're not a module, we must make sure everything is setup before anyone */
> >> +/* tries to use us ... */
> >> +#ifndef MODULE
> >> +subsys_initcall(mpc52xx_bcom_init);
> >> +#endif
> >
> > You can make it subsys_initcall() unconditionally. If the driver gets built
> > as a module, it will turn into module_init() by itself.
> Oh, great, I didn't know that. I guess the module_exit still has to be
> conditionnal.
No, it's designed so that you don't need any #ifdef. The module_exit
function can simply be left out entirely when doing a builtin driver.
> > Why don't you use EXPORT_SYMBOL_GPL?
> Why would I ? Is it mandatory now ?
> I don't really have an objection to non-gpl modules to use the exported
> functions ...
It's your choice, but there is a strong recommendation to use _GPL for
all new interfaces nowadays. If you don't, people might suspect that
you have a hidden agenda and actually plan to do non-gpl modules
yourself.
However, this is a legal minefield and I'm not trying to force you
to change it, it is always the decision of the copyright holder.
Arnd <><
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 23:49 ` Sylvain Munaut
2007-05-13 0:24 ` Arnd Bergmann
@ 2007-05-13 3:36 ` Dale Farnsworth
1 sibling, 0 replies; 24+ messages in thread
From: Dale Farnsworth @ 2007-05-13 3:36 UTC (permalink / raw)
To: tnt; +Cc: linuxppc-dev, Arnd Bergmann
Sylvain Munaut wrote:
> Arnd Bergmann wrote:
> > Why don't you use EXPORT_SYMBOL_GPL?
> Why would I ? Is it mandatory now ?
> I don't really have an objection to non-gpl modules to use the exported
> functions ...
> If Dale or Andrey have, they're free to post a patch to change to _GPL.
Hi Sylvain,
The general recommendation is to use EXPORT_SYMBOL_GPL unless there
is some overriding reason not to. I agree with that recommendation.
That's my preference for this code, but I'll defer to you Sylvain.
BTW, it's great to see this code being submitted.
-Dale Farnsworth
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-13 0:24 ` Arnd Bergmann
@ 2007-05-13 7:17 ` Sylvain Munaut
2007-05-13 23:29 ` Matt Sealey
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-13 7:17 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linuxppc-dev
>> However, you could totally implement a "DMA devices" that would just
>> use a simple "copy from there to there" task using this BestComm driver.
>> So other part of the kernel (like network) could use that interface to
>> use the dma engine ...
>>
>
> Can it also do the advanced operations like XOR on DMA that the
> Intel ioat has? That would be even more interesting, I guess.
>
Actually yes it can.
It just need someone motivated enough to write it ;)
>>>> +static int __init
>>>> +mpc52xx_bcom_init(void)
>>>> +{
>>>> + struct device_node *ofn_bcom, *ofn_sram;
>>>> + struct resource res_bcom;
>>>> +
>>>> + int rv;
>>>> +
>>>> + /* Find the bestcomm node. If none, fails 'silently' since
>>>> + * we may just be on another platform */
>>>> + ofn_bcom = of_find_compatible_node(
>>>> + NULL, "dma-controller", "mpc5200-bestcomm");
>>>> + if (!ofn_bcom)
>>>> + return -ENODEV;
>>>>
>>> I know, my usual rant is getting old, but why is this one not an
>>> of_platform_driver? It's not shared with arch/ppc or with arch/mips,
>>> and it's not needed before module_init() time.
>>>
>> It needs to be initialized before _any_ other driver that uses bestcomm.
>> When compiled as module that could be an of_platform_driver but when
>> built-in there is apparently no way to ensure it's going to be probed
>> first. (At least no clean way ... )
>>
>
> Initialization order is always a little tricky, but if you use a
> subsys_initcall, it should at least come before any regular driver
> like network or disk, and arch/ drivers come before any code in drivers/,
> so I don't see much of a problem here.
>
Mmm ... Silly of me but I didn't think of that.
I'll give it a shot.
(Actually before the transition to arch/powerpc, this was a
platform_driver ...
but there I could control the probe order with ppc_sys ).
>>> Why don't you use EXPORT_SYMBOL_GPL?
>>>
>> Why would I ? Is it mandatory now ?
>> I don't really have an objection to non-gpl modules to use the exported
>> functions ...
>>
>
> It's your choice, but there is a strong recommendation to use _GPL for
> all new interfaces nowadays. If you don't, people might suspect that
> you have a hidden agenda and actually plan to do non-gpl modules
> yourself.
>
Well if it's recommended ... And since Dale is in favor of it, so be it,
I don't have any hidden agenda ;)
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-13 7:17 ` Sylvain Munaut
@ 2007-05-13 23:29 ` Matt Sealey
2007-05-14 5:15 ` Sylvain Munaut
0 siblings, 1 reply; 24+ messages in thread
From: Matt Sealey @ 2007-05-13 23:29 UTC (permalink / raw)
To: Sylvain Munaut; +Cc: linuxppc-dev, Arnd Bergmann
Sylvain Munaut wrote:
>>> However, you could totally implement a "DMA devices" that would just
>>> use a simple "copy from there to there" task using this BestComm driver.
>>> So other part of the kernel (like network) could use that interface to
>>> use the dma engine ...
>>>
>> Can it also do the advanced operations like XOR on DMA that the
>> Intel ioat has? That would be even more interesting, I guess.
>>
> Actually yes it can.
> It just need someone motivated enough to write it ;)
Isn't there an XOR-while-copying-DMA example somewhere?
I'm sure I've seen reference to it, or it being used somewhere.. to
be honest the worst part of using BestComm is it's only well documented
on how to use tasks and how to set up the engine, actually writing them
is some kind of black art which Freescale hold close to their chest.
--
Matt Sealey <matt@genesi-usa.com>
Genesi, Manager, Developer Relations
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes
2007-05-12 20:31 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Sylvain Munaut
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
@ 2007-05-13 23:46 ` Matt Sealey
2007-05-14 5:27 ` Sylvain Munaut
1 sibling, 1 reply; 24+ messages in thread
From: Matt Sealey @ 2007-05-13 23:46 UTC (permalink / raw)
To: Sylvain Munaut; +Cc: Linux PPC dev ML
Would a note next to these new fields be worth it? "5200B only" or
"reserved on 5200" or even create a new PSC structure for the old
processor and recast it in drivers that really want to support both
revisions of the CPU?
Real nitpick:
Setting the CCR on the MPC5200 (not B) now means you need to left
shift your required CCR values by 16 bits before applying them into
the field for the desired effect.
If you were being lazy and just wanted to set some low-order bits
in the CCR on the 5200, with the old structure that's fine. With
the new structure, it will be writing into previously reserved
space on the 5200. The 5200B might work fine, unless it's a driver
someone missed, and is programming a value that should be in the
upper 16 bits of the new CCR, in which case now you have to left
shift by 16 bits.. :)
I don't suppose it matters a great deal (who uses an original MPC5200?),
but it would be an intrusive change if any drivers did actually use the
CCR already.. it's nice to mention somewhere though, I guess.
--
Matt Sealey <matt@genesi-usa.com>
Genesi, Manager, Developer Relations
Sylvain Munaut wrote:
> On the mpc5200b the ccr register is 32 bits wide while on the
> mpc5200 it's only 16 bits. It's up to the driver to use the
> correct format depending on the chip it's running on.
>
> The 5200b also offers some more registers & status in AC97
> mode. Again, if not running on a 5200b the driver should not
> use those.
>
> Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
> ---
> include/asm-ppc/mpc52xx_psc.h | 10 ++++++++--
> 1 files changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/include/asm-ppc/mpc52xx_psc.h b/include/asm-ppc/mpc52xx_psc.h
> index 9d850b2..c82b8d4 100644
> --- a/include/asm-ppc/mpc52xx_psc.h
> +++ b/include/asm-ppc/mpc52xx_psc.h
> @@ -28,6 +28,10 @@
> #define MPC52xx_PSC_MAXNUM 6
>
> /* Programmable Serial Controller (PSC) status register bits */
> +#define MPC52xx_PSC_SR_UNEX_RX 0x0001
> +#define MPC52xx_PSC_SR_DATA_VAL 0x0002
> +#define MPC52xx_PSC_SR_DATA_OVR 0x0004
> +#define MPC52xx_PSC_SR_CMDSEND 0x0008
> #define MPC52xx_PSC_SR_CDE 0x0080
> #define MPC52xx_PSC_SR_RXRDY 0x0100
> #define MPC52xx_PSC_SR_RXFULL 0x0200
> @@ -132,8 +136,10 @@ struct mpc52xx_psc {
> u8 reserved5[3];
> u8 ctlr; /* PSC + 0x1c */
> u8 reserved6[3];
> - u16 ccr; /* PSC + 0x20 */
> - u8 reserved7[14];
> + u32 ccr; /* PSC + 0x20 */
> + u32 ac97_slots; /* PSC + 0x24 */
> + u32 ac97_cmd; /* PSC + 0x28 */
> + u32 ac97_data; /* PSC + 0x2c */
> u8 ivr; /* PSC + 0x30 */
> u8 reserved8[3];
> u8 ip; /* PSC + 0x34 */
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-13 23:29 ` Matt Sealey
@ 2007-05-14 5:15 ` Sylvain Munaut
0 siblings, 0 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-14 5:15 UTC (permalink / raw)
To: Matt Sealey; +Cc: linuxppc-dev, Arnd Bergmann
Matt Sealey wrote:
> Sylvain Munaut wrote:
>
>>>> However, you could totally implement a "DMA devices" that would just
>>>> use a simple "copy from there to there" task using this BestComm driver.
>>>> So other part of the kernel (like network) could use that interface to
>>>> use the dma engine ...
>>>>
>>>>
>>> Can it also do the advanced operations like XOR on DMA that the
>>> Intel ioat has? That would be even more interesting, I guess.
>>>
>>>
>> Actually yes it can.
>> It just need someone motivated enough to write it ;)
>>
>
> Isn't there an XOR-while-copying-DMA example somewhere?
>
Never saw it. But in the gen BD task it's easy to find the DRD that
really does the copy and using the hand assembly guide and the
example DRD than auto-shutdown the task, it's possible to figure out
how to replace that simple DRD by a one calling xor in EU3.
> I'm sure I've seen reference to it, or it being used somewhere.. to
> be honest the worst part of using BestComm is it's only well documented
> on how to use tasks and how to set up the engine, actually writing them
> is some kind of black art which Freescale hold close to their chest.
>
No kidding ;)
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes
2007-05-13 23:46 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Matt Sealey
@ 2007-05-14 5:27 ` Sylvain Munaut
2007-05-15 10:59 ` Raquel Velasco and Bill Buck
0 siblings, 1 reply; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-14 5:27 UTC (permalink / raw)
To: Matt Sealey; +Cc: Linux PPC dev ML
Matt Sealey wrote:
> Would a note next to these new fields be worth it? "5200B only" or
> "reserved on 5200" or even create a new PSC structure for the old
> processor and recast it in drivers that really want to support both
> revisions of the CPU?
>
A comment next to them yes, that looks a good idea.
A new structure certainly not.
> Real nitpick:
>
> Setting the CCR on the MPC5200 (not B) now means you need to left
> shift your required CCR values by 16 bits before applying them into
> the field for the desired effect.
>
> If you were being lazy and just wanted to set some low-order bits
> in the CCR on the 5200, with the old structure that's fine. With
> the new structure, it will be writing into previously reserved
> space on the 5200. The 5200B might work fine, unless it's a driver
> someone missed, and is programming a value that should be in the
> upper 16 bits of the new CCR, in which case now you have to left
> shift by 16 bits.. :)
I know about the ccr that's annoying.
I guess I could make ccr a union and have it contains ccr16 and ccr32.
Note that old driver will still work ... then most likely do
out_be16(&psc->ccr, ...) and the &psc->ccr address didn't change. They
will just have a warning because ccr is a u32 so &psc->ccr is a u32* and
they give it to out_be16 ...
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes
2007-05-14 5:27 ` Sylvain Munaut
@ 2007-05-15 10:59 ` Raquel Velasco and Bill Buck
0 siblings, 0 replies; 24+ messages in thread
From: Raquel Velasco and Bill Buck @ 2007-05-15 10:59 UTC (permalink / raw)
To: Sylvain Munaut; +Cc: Linux PPC dev ML
Sylvain, thanks.
http://bbrv.blogspot.com/2007/05/not-waiting-for-world-to-change-ii.html
Read through the post. Your effort is being applied to good use.
R&B
On May 14, 2007, at 12:27 AM, Sylvain Munaut wrote:
> Matt Sealey wrote:
>> Would a note next to these new fields be worth it? "5200B only" or
>> "reserved on 5200" or even create a new PSC structure for the old
>> processor and recast it in drivers that really want to support both
>> revisions of the CPU?
>>
> A comment next to them yes, that looks a good idea.
> A new structure certainly not.
>> Real nitpick:
>>
>> Setting the CCR on the MPC5200 (not B) now means you need to left
>> shift your required CCR values by 16 bits before applying them into
>> the field for the desired effect.
>>
>> If you were being lazy and just wanted to set some low-order bits
>> in the CCR on the 5200, with the old structure that's fine. With
>> the new structure, it will be writing into previously reserved
>> space on the 5200. The 5200B might work fine, unless it's a driver
>> someone missed, and is programming a value that should be in the
>> upper 16 bits of the new CCR, in which case now you have to left
>> shift by 16 bits.. :)
> I know about the ccr that's annoying.
> I guess I could make ccr a union and have it contains ccr16 and ccr32.
>
> Note that old driver will still work ... then most likely do
> out_be16(&psc->ccr, ...) and the &psc->ccr address didn't change. They
> will just have a warning because ccr is a u32 so &psc->ccr is a
> u32* and
> they give it to out_be16 ...
>
>
> Sylvain
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
Raquel and Bill
http://bbrv.blogspot.com
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 2/9] powerpc: Changes the config mechanism for rheap
2007-05-12 20:31 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Sylvain Munaut
2007-05-12 20:31 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Sylvain Munaut
@ 2007-05-15 21:20 ` Kumar Gala
1 sibling, 0 replies; 24+ messages in thread
From: Kumar Gala @ 2007-05-15 21:20 UTC (permalink / raw)
To: Sylvain Munaut; +Cc: Linux PPC dev ML
On May 12, 2007, at 3:31 PM, Sylvain Munaut wrote:
> Instead of having in the makefile all the option that
> requires rheap, we define a configuration symbol
> and when needed we make sure it's selected.
>
> Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
> ---
> arch/powerpc/Kconfig | 2 ++
> arch/powerpc/lib/Kconfig | 3 +++
> arch/powerpc/lib/Makefile | 4 +---
> arch/powerpc/platforms/Kconfig | 2 ++
> 4 files changed, 8 insertions(+), 3 deletions(-)
> create mode 100644 arch/powerpc/lib/Kconfig
You need to fixup arch/ppc as well since its now using rheap from
arch/powerpc/lib.
- k
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 56d3c0d..094e5d2 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -193,6 +193,7 @@ config PPC_8xx
> bool "Freescale 8xx"
> select FSL_SOC
> select 8xx
> + select PPC_LIB_RHEAP
>
> config 40x
> bool "AMCC 40x"
> @@ -885,6 +886,7 @@ source "fs/Kconfig"
>
> source "arch/powerpc/sysdev/qe_lib/Kconfig"
>
> +source "arch/powerpc/lib/Kconfig"
> source "lib/Kconfig"
>
> menu "Instrumentation Support"
> diff --git a/arch/powerpc/lib/Kconfig b/arch/powerpc/lib/Kconfig
> new file mode 100644
> index 0000000..f383ad4
> --- /dev/null
> +++ b/arch/powerpc/lib/Kconfig
> @@ -0,0 +1,3 @@
> +config PPC_LIB_RHEAP
> + bool
> + default n
> diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
> index 0a486d4..a6cf399 100644
> --- a/arch/powerpc/lib/Makefile
> +++ b/arch/powerpc/lib/Makefile
> @@ -13,7 +13,6 @@ endif
>
> obj-$(CONFIG_PPC64) += checksum_64.o copypage_64.o copyuser_64.o \
> memcpy_64.o usercopy_64.o mem_64.o string.o
> -obj-$(CONFIG_QUICC_ENGINE) += rheap.o
> obj-$(CONFIG_XMON) += sstep.o
> obj-$(CONFIG_KPROBES) += sstep.o
> obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
> @@ -23,5 +22,4 @@ obj-$(CONFIG_SMP) += locks.o
> endif
>
> # Temporary hack until we have migrated to asm-powerpc
> -obj-$(CONFIG_8xx) += rheap.o
> -obj-$(CONFIG_CPM2) += rheap.o
> +obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/
> platforms/Kconfig
> index 361acfa..8432f56 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -242,6 +242,7 @@ config TAU_AVERAGE
>
> config QUICC_ENGINE
> bool
> + select PPC_LIB_RHEAP
> help
> The QUICC Engine (QE) is a new generation of communications
> coprocessors on Freescale embedded CPUs (akin to CPM in older
> chips).
> @@ -251,6 +252,7 @@ config QUICC_ENGINE
> config CPM2
> bool
> default n
> + select PPC_LIB_RHEAP
> help
> The CPM2 (Communications Processor Module) is a coprocessor on
> embedded CPUs made by Freescale. Selecting this option means that
> --
> 1.5.1.2
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
2007-05-12 23:27 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Arnd Bergmann
@ 2007-05-15 21:37 ` Kumar Gala
2007-05-15 22:27 ` Sylvain Munaut
2 siblings, 1 reply; 24+ messages in thread
From: Kumar Gala @ 2007-05-15 21:37 UTC (permalink / raw)
To: Sylvain Munaut; +Cc: Linux PPC dev ML
On May 12, 2007, at 3:31 PM, Sylvain Munaut wrote:
> This patch adds support for the core of the BestComm API
> for the Freescale MPC5200(b). The BestComm engine is a
> microcode-controlled / tasks-based DMA used by several
> of the onchip devices.
>
> Setting up the tasks / memory allocation and all common
> low level functions are handled by this patch.
> The specifics details of each tasks and their microcode
> are split-out in separate patches.
>
> This is not the official API, but a much cleaner one.
Can you give more detail about how the API works.
> Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
> ---
> arch/powerpc/platforms/Kconfig | 2 +
> arch/powerpc/sysdev/Makefile | 1 +
> arch/powerpc/sysdev/bestcomm/Kconfig | 18 +
> arch/powerpc/sysdev/bestcomm/Makefile | 8 +
> arch/powerpc/sysdev/bestcomm/bestcomm.c | 600 +++++++++++++++
> +++++++++++
> arch/powerpc/sysdev/bestcomm/bestcomm.h | 136 ++++++
> arch/powerpc/sysdev/bestcomm/bestcomm_priv.h | 325 ++++++++++++++
> arch/powerpc/sysdev/bestcomm/sram.c | 180 ++++++++
> arch/powerpc/sysdev/bestcomm/sram.h | 54 +++
> 9 files changed, 1324 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/sysdev/bestcomm/Kconfig
> create mode 100644 arch/powerpc/sysdev/bestcomm/Makefile
> create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.c
> create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.h
> create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
> create mode 100644 arch/powerpc/sysdev/bestcomm/sram.c
> create mode 100644 arch/powerpc/sysdev/bestcomm/sram.h
>
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/
> platforms/Kconfig
> index 8432f56..fc170a3 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -259,4 +259,6 @@ config CPM2
> you wish to build a kernel for a machine with a CPM2 coprocessor
> on it (826x, 827x, 8560).
>
> +source "arch/powerpc/sysdev/bestcomm/Kconfig"
> +
> endmenu
> diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/
> Makefile
> index c3ce0bd..89074f8 100644
> --- a/arch/powerpc/sysdev/Makefile
> +++ b/arch/powerpc/sysdev/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o
> obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o
> obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
> obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
> +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
> mv64x60-$(CONFIG_PCI) += mv64x60_pci.o
> obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o
>
> diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/
> sysdev/bestcomm/Kconfig
> new file mode 100644
> index 0000000..3366e24
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/Kconfig
> @@ -0,0 +1,18 @@
> +#
> +# Kconfig options for Bestcomm
> +#
> +
> +config PPC_BESTCOMM
> + tristate "Bestcomm DMA engine support"
> + depends on PPC_MPC52xx
> + default n
> + select PPC_LIB_RHEAP
> + help
> + BestComm is the name of the communication coprocessor found
> + on the Freescale MPC5200 family of processor. It's usage is
> + optionnal for some drivers (like ATA), but required for
> + others (like FEC).
> +
> + If you want to use drivers that require DMA operations,
> + answer Y or M. Otherwise say N.
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/
> sysdev/bestcomm/Makefile
> new file mode 100644
> index 0000000..a24aa06
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for BestComm & co
> +#
> +
> +bestcomm-core-objs := bestcomm.o sram.o
> +
> +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
Any reason why sram isn't on the ojb-$(CONFIG_PPC_BESTCOMM) line?
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/
> sysdev/bestcomm/bestcomm.c
> new file mode 100644
> index 0000000..0063a1e
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c
> @@ -0,0 +1,600 @@
> +/*
> + * Driver for MPC52xx processor BestComm peripheral controller
> + *
> + *
> + * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
> + * Copyright (C) 2005 Varma Electronics Oy,
> + * ( by Andrey Volkov <avolkov@varma-
> el.com> )
> + * Copyright (C) 2003-2004 MontaVista, Software, Inc.
> + * ( by Dale Farnsworth
> <dfarnsworth@mvista.com> )
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/prom.h>
> +#include <asm/mpc52xx.h>
> +
> +#include "sram.h"
> +#include "bestcomm_priv.h"
> +#include "bestcomm.h"
> +
> +#define DRIVER_NAME "bestcomm-core"
> +
> +
> +struct bcom_engine *bcom = NULL;
> +
> +
> +/*
> ======================================================================
> == */
> +/* Public and private
> API */
> +/*
> ======================================================================
> == */
> +
> +/* Debug Dump */
> +
> +#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a,
> ## b)
We have dev_dbg and dev_printk can we not use them?
> +
> +void
> +bcom_dump_status(void)
> +{
> + int i;
> + struct mpc52xx_sdma __iomem *r = bcom->regs;
> +
> + BCOM_DPRINTK("BestComm status dump (pa=%08lx, va=%p)\n",
> + bcom->regs_base, bcom->regs);
> + BCOM_DPRINTK(" taskBar = %08x\n", in_be32(&r->taskBar));
> + BCOM_DPRINTK(" currentPointer = %08x\n", in_be32(&r-
> >currentPointer));
> + BCOM_DPRINTK(" endPointer = %08x\n", in_be32(&r->endPointer));
> + BCOM_DPRINTK(" variablePointer = %08x\n", in_be32(&r-
> >variablePointer));
> + BCOM_DPRINTK(" IntVect1 = %08x\n", (u32)in_8(&r->IntVect1));
> + BCOM_DPRINTK(" IntVect2 = %08x\n", (u32)in_8(&r->IntVect2));
> + BCOM_DPRINTK(" PtdCntrl = %08hx\n", in_be16(&r->PtdCntrl));
> + BCOM_DPRINTK(" IntPend = %08x\n", in_be32(&r->IntPend));
> + BCOM_DPRINTK(" IntMask = %08x\n", in_be32(&r->IntMask));
> +
> + BCOM_DPRINTK(" TCR dump :\n");
> +
> + for (i=0; i<16; i++) {
> + printk("%s%04hx%s",
> + (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "",
> + in_be16(&r->tcr[i]),
> + (i&0x7) == 0x7 ? "\n" : " ");
> + }
> +
> + BCOM_DPRINTK(" IPR dump :\n");
> +
> + for (i=0; i<32; i++) {
> + printk("%s%02x%s",
> + (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "",
> + (u32)in_8(&r->ipr[i]),
> + (i&0x7) == 0x7 ? "\n" : " ");
> + }
> +
> + BCOM_DPRINTK(" cReqSelect = %08x\n", in_be32(&r->cReqSelect));
> + BCOM_DPRINTK(" task_size0 = %08x\n", in_be32(&r->task_size0));
> + BCOM_DPRINTK(" task_size1 = %08x\n", in_be32(&r->task_size1));
> + BCOM_DPRINTK(" MDEDebug = %08x\n", in_be32(&r->MDEDebug));
> + BCOM_DPRINTK(" ADSDebug = %08x\n", in_be32(&r->ADSDebug));
> + BCOM_DPRINTK(" Value1 = %08x\n", in_be32(&r->Value1));
> + BCOM_DPRINTK(" Value2 = %08x\n", in_be32(&r->Value2));
> + BCOM_DPRINTK(" Control = %08x\n", in_be32(&r->Control));
> + BCOM_DPRINTK(" Status = %08x\n", in_be32(&r->Status));
> + BCOM_DPRINTK(" PTDDebug = %08x\n", in_be32(&r->PTDDebug));
> +}
> +
> +void
> +bcom_dump_task(int task)
> +{
> + int i;
> + u32 *p;
> + struct bcom_tdt *tdt = &bcom->tdt[task];
> +
> + BCOM_DPRINTK("Task dump %d\n", task);
> + BCOM_DPRINTK(" tcr = %04hx\n", bcom->regs->tcr[task]);
> + BCOM_DPRINTK(" tdt = %p\n", &bcom->tdt[task]);
> + BCOM_DPRINTK(" tdt->start = %08x\n", tdt->start);
> + BCOM_DPRINTK(" tdt->stop = %08x\n", tdt->stop);
> + BCOM_DPRINTK(" tdt->var = %08x\n", tdt->var);
> + BCOM_DPRINTK(" tdt->fdt = %08x\n", tdt->fdt);
> + BCOM_DPRINTK(" tdt->status = %08x\n", tdt->exec_status);
> + BCOM_DPRINTK(" tdt->mvtp = %08x\n", tdt->mvtp);
> + BCOM_DPRINTK(" tdt->context = %08x\n", tdt->context);
> + BCOM_DPRINTK(" tdt->litbase = %08x\n", tdt->litbase);
> +
> + BCOM_DPRINTK(" code :\n");
> +
> + p = bcom_task_desc(task);
> + for (i=0; i<bcom_task_num_descs(task); i++)
> + printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]);
> +
> + BCOM_DPRINTK(" var/inc :\n");
> +
> + p = bcom_task_var(task);
> + for (i=0; i<BCOM_MAX_VAR+BCOM_MAX_INC; i++)
> + printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]);
> +}
> +
> +void
> +bcom_dump_bdring(struct bcom_task *tsk)
> +{
> + int i, j;
> +
> + BCOM_DPRINTK("BD ring dump %d\n", tsk->tasknum);
> +
> + for (i=0; i<tsk->num_bd; i++) {
> + BCOM_DPRINTK(" BD[%02d] :\n", i);
> + BCOM_DPRINTK(" cookie : %p\n", tsk->cookie[i]);
> + BCOM_DPRINTK(" status : %08x\n", tsk->bd[i].status);
> + for (j=0; j<(tsk->bd_size/sizeof(u32))-1; j++)
> + BCOM_DPRINTK(" data[%02d] : %08x\n",
> + j, tsk->bd[i].data[j]);
> + }
> +}
> +
> +
Would all bcom_dump_status(), bcom_dump_task(), bcom_dump_bdring be
better using debugfs? At minimum there should be a Kconfig option to
enable bestcomm debug that enables this code.
> +/* Private API */
> +
What's private about it?
It would probably be good for the API functions to have DocBook style
comments. See something like drivers/rapidio/rio.c for an example.
> +struct bcom_task *
> +bcom_task_alloc(int bd_count, int bd_size, int priv_size)
> +{
> + int i, tasknum = -1;
> + struct bcom_task *tsk;
> +
> + /* Get and reserve a task num */
> + spin_lock(&bcom->lock);
> +
> + for (i=0; i<BCOM_MAX_TASKS; i++)
> + if (!bcom->tdt[i].stop) { /* we use stop as a marker */
> + bcom->tdt[i].stop = 0xfffffffful; /* dummy addr */
> + tasknum = i;
> + break;
> + }
> +
> + spin_unlock(&bcom->lock);
> +
> + if (tasknum < 0)
> + return NULL;
> +
> + /* Allocate our structure */
> + tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);
> + if (!tsk)
> + goto error;
> +
> + tsk->tasknum = tasknum;
> + if (priv_size)
> + tsk->priv = (void*)tsk + sizeof(struct bcom_task);
> +
> + /* Get IRQ of that task */
> + tsk->irq = irq_of_parse_and_map(bcom->ofnode, tsk->tasknum);
> + if (tsk->irq == NO_IRQ)
> + goto error;
> +
> + /* Init the BDs, if needed */
> + if (bd_count) {
> + tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL);
> + if (!tsk->cookie)
> + goto error;
> +
> + tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);
> + if (!tsk->bd)
> + goto error;
> + memset(tsk->bd, 0x00, bd_count * bd_size);
> +
> + tsk->num_bd = bd_count;
> + tsk->bd_size = bd_size;
> + }
> +
> + return tsk;
> +
> +error:
> + if (tsk) {
> + if (tsk->irq != NO_IRQ)
> + irq_dispose_mapping(tsk->irq);
> + bcom_sram_free(tsk->bd);
> + kfree(tsk->cookie);
> + kfree(tsk);
> + }
> +
> + bcom->tdt[tasknum].stop = 0;
> +
> + return NULL;
> +}
> +
> +void
> +bcom_task_release(struct bcom_task *tsk)
bcom_task_free() to match alloc/free semantics?
> +{
> + /* Stop the task */
> + bcom_disable_task(tsk->tasknum);
> +
> + /* Clear TDT */
> + bcom->tdt[tsk->tasknum].start = 0;
> + bcom->tdt[tsk->tasknum].stop = 0;
> +
> + /* Free everything */
> + irq_dispose_mapping(tsk->irq);
> + bcom_sram_free(tsk->bd);
> + kfree(tsk->cookie);
> + kfree(tsk);
> +}
> +
> +int
> +bcom_load_image(int task, u32 *task_image)
> +{
> + struct bcom_task_header *hdr = (struct bcom_task_header *)
> task_image;
> + struct bcom_tdt *tdt;
> + u32 *desc, *var, *inc;
> + u32 *desc_src, *var_src, *inc_src;
> +
> + /* Safety checks */
> + if (hdr->magic != BCOM_TASK_MAGIC) {
> + printk(KERN_ERR DRIVER_NAME
> + ": Trying to load invalid microcode\n");
> + return -EINVAL;
> + }
> +
> + if ((task < 0) || (task >= BCOM_MAX_TASKS)) {
> + printk(KERN_ERR DRIVER_NAME
> + ": Trying to load invalid task %d\n", task);
> + return -EINVAL;
> + }
> +
> + /* Initial load or reload */
> + tdt = &bcom->tdt[task];
> +
> + if (tdt->start) {
> + desc = bcom_task_desc(task);
> + if (hdr->desc_size != bcom_task_num_descs(task)) {
> + printk(KERN_ERR DRIVER_NAME
> + ": Trying to reload wrong task image "
> + "(%d size %d/%d)!\n",
> + task,
> + hdr->desc_size,
> + bcom_task_num_descs(task));
> + return -EINVAL;
> + }
> + } else {
> + phys_addr_t start_pa;
> +
> + desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);
> + if (!desc)
> + return -ENOMEM;
> +
> + tdt->start = start_pa;
> + tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));
> + }
> +
> + var = bcom_task_var(task);
> + inc = bcom_task_inc(task);
> +
> + /* Clear & copy */
> + memset(var, 0x00, BCOM_VAR_SIZE);
> + memset(inc, 0x00, BCOM_INC_SIZE);
> +
> + desc_src = (u32 *)(hdr + 1);
> + var_src = desc_src + hdr->desc_size;
> + inc_src = var_src + hdr->var_size;
> +
> + memcpy(desc, desc_src, hdr->desc_size * sizeof(u32));
> + memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));
> + memcpy(inc, inc_src, hdr->inc_size * sizeof(u32));
> +
> + return 0;
> +}
> +
> +void
> +bcom_set_initiator(int task, int initiator)
> +{
> + int i;
> + int num_descs;
> + u32 *desc;
> + int next_drd_has_initiator;
> +
> + bcom_set_tcr_initiator(task, initiator);
> +
> + /* Just setting tcr is apparently not enough due to some problem */
> + /* with it. So we just go thru all the microcode and replace in */
> + /* the DRD directly */
> +
> + desc = bcom_task_desc(task);
> + next_drd_has_initiator = 1;
> + num_descs = bcom_task_num_descs(task);
> +
> + for (i=0; i<num_descs; i++, desc++) {
> + if (!bcom_desc_is_drd(*desc))
> + continue;
> + if (next_drd_has_initiator)
> + if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)
> + bcom_set_desc_initiator(desc, initiator);
> + next_drd_has_initiator = !bcom_drd_is_extended(*desc);
> + }
> +}
> +
> +
> +/* Public API */
> +
What's public about these?
> +void
> +bcom_enable(struct bcom_task *tsk)
> +{
> + bcom_enable_task(tsk->tasknum);
> +}
> +
> +void
> +bcom_disable(struct bcom_task *tsk)
> +{
> + bcom_disable_task(tsk->tasknum);
> +}
> +
> +
> +/*
> ======================================================================
> == */
> +/* Engine init/
> cleanup */
> +/*
> ======================================================================
> == */
> +
> +/* Function Descriptor table */
> +/* this will need to be updated if Freescale changes their task
> code FDT */
> +static u32 fdt_ops[] = {
> + 0xa0045670, /* FDT[48] - load_acc() */
> + 0x80045670, /* FDT[49] - unload_acc() */
> + 0x21800000, /* FDT[50] - and() */
> + 0x21e00000, /* FDT[51] - or() */
> + 0x21500000, /* FDT[52] - xor() */
> + 0x21400000, /* FDT[53] - andn() */
> + 0x21500000, /* FDT[54] - not() */
> + 0x20400000, /* FDT[55] - add() */
> + 0x20500000, /* FDT[56] - sub() */
> + 0x20800000, /* FDT[57] - lsh() */
> + 0x20a00000, /* FDT[58] - rsh() */
> + 0xc0170000, /* FDT[59] - crc8() */
> + 0xc0145670, /* FDT[60] - crc16() */
> + 0xc0345670, /* FDT[61] - crc32() */
> + 0xa0076540, /* FDT[62] - endian32() */
> + 0xa0000760, /* FDT[63] - endian16() */
> +};
> +
> +
> +static int __devinit
> +bcom_engine_init(void)
> +{
> + int task;
> + phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
> + unsigned int tdt_size, ctx_size, var_size, fdt_size;
> +
> + /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/
> incs */
> + tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
> + ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
> + var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
> + fdt_size = BCOM_FDT_SIZE;
> +
> + bcom->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
> + bcom->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
> + bcom->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
> + bcom->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
> +
> + if (!bcom->tdt || !bcom->ctx || !bcom->var || !bcom->fdt) {
> + printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
> +
> + bcom_sram_free(bcom->tdt);
> + bcom_sram_free(bcom->ctx);
> + bcom_sram_free(bcom->var);
> + bcom_sram_free(bcom->fdt);
> +
> + return -ENOMEM;
> + }
> +
> + memset(bcom->tdt, 0x00, tdt_size);
> + memset(bcom->ctx, 0x00, ctx_size);
> + memset(bcom->var, 0x00, var_size);
> + memset(bcom->fdt, 0x00, fdt_size);
> +
> + /* Copy the FDT for the EU#3 */
> + memcpy(&bcom->fdt[48], fdt_ops, sizeof(fdt_ops));
> +
> + /* Initialize Task base structure */
> + for (task=0; task<BCOM_MAX_TASKS; task++)
> + {
> + out_be16(&bcom->regs->tcr[task], 0);
> + out_8(&bcom->regs->ipr[task], 0);
> +
> + bcom->tdt[task].context = ctx_pa;
> + bcom->tdt[task].var = var_pa;
> + bcom->tdt[task].fdt = fdt_pa;
> +
> + var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
> + ctx_pa += BCOM_CTX_SIZE;
> + }
> +
> + out_be32(&bcom->regs->taskBar, tdt_pa);
> +
> + /* Init 'always' initiator */
> + out_8(&bcom->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
> +
> + /* Disable COMM Bus Prefetch, apparently it's not reliable yet */
> + /* FIXME: This should be done on 5200 and not 5200B ... */
> + out_be16(&bcom->regs->PtdCntrl, in_be16(&bcom->regs->PtdCntrl) | 1);
> +
> + /* Init lock */
> + spin_lock_init(&bcom->lock);
> +
> + return 0;
> +}
> +
> +static void
> +bcom_engine_cleanup(void)
> +{
> + int task;
> +
> + /* Stop all tasks */
> + for (task=0; task<BCOM_MAX_TASKS; task++)
> + {
> + out_be16(&bcom->regs->tcr[task], 0);
> + out_8(&bcom->regs->ipr[task], 0);
> + }
> +
> + out_be32(&bcom->regs->taskBar, 0ul);
> +
> + /* Release the SRAM zones */
> + bcom_sram_free(bcom->tdt);
> + bcom_sram_free(bcom->ctx);
> + bcom_sram_free(bcom->var);
> + bcom_sram_free(bcom->fdt);
> +}
> +
> +
> +/*
> ======================================================================
> == */
> +/* System/Module init &
> cleanup */
> +/*
> ======================================================================
> == */
> +
> +static int __init
> +mpc52xx_bcom_init(void)
> +{
> + struct device_node *ofn_bcom, *ofn_sram;
> + struct resource res_bcom;
> +
> + int rv;
> +
> + /* Find the bestcomm node. If none, fails 'silently' since
> + * we may just be on another platform */
> + ofn_bcom = of_find_compatible_node(
> + NULL, "dma-controller", "mpc5200-bestcomm");
> + if (!ofn_bcom)
> + return -ENODEV;
> +
> + /* Inform user we're ok so far */
> + printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
> +
> + /* Prepare SRAM */
> + ofn_sram = of_find_compatible_node(NULL, "sram", "mpc5200-sram");
> + if (!ofn_sram) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "No SRAM found in device tree\n");
> + rv = -ENODEV;
> + goto error_ofput;
> + }
> +
> + rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
> +
> + of_node_put(ofn_sram);
> +
> + if (rv) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "Error in SRAM init\n");
> + goto error_ofput;
> + }
> +
> + /* Get a clean struct */
> + bcom = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
> + if (!bcom) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "Can't allocate state structure\n");
> + rv = -ENOMEM;
> + goto error_sramclean;
> + }
> +
> + /* Save the node */
> + bcom->ofnode = ofn_bcom;
> +
> + /* Get, reserve & map io */
> + if (of_address_to_resource(bcom->ofnode, 0, &res_bcom)) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "Can't get resource\n");
> + rv = -EINVAL;
> + goto error_sramclean;
> + }
> +
> + if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma),
> + DRIVER_NAME)) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "Can't request registers region\n");
> + rv = -EBUSY;
> + goto error_sramclean;
> + }
> +
> + bcom->regs_base = res_bcom.start;
> + bcom->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
> + if (!bcom->regs) {
> + printk(KERN_ERR DRIVER_NAME ": "
> + "Can't map registers\n");
> + rv = -ENOMEM;
> + goto error_release;
> + }
> +
> + /* Now, do the real init */
> + rv = bcom_engine_init();
> + if (rv)
> + goto error_unmap;
> +
> + /* Done ! */
> + printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
> + bcom->regs_base);
> +
> + return 0;
> +
> + /* Error path */
> +error_unmap:
> + iounmap(bcom->regs);
> +error_release:
> + release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
> +error_sramclean:
> + bcom_sram_cleanup();
> +error_ofput:
> + of_node_put(bcom->ofnode);
> +
> + printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
> +
> + return rv;
> +}
> +
> +static void __exit
> +mpc52xx_bcom_exit(void)
> +{
> + /* Clean up the engine */
> + bcom_engine_cleanup();
> +
> + /* Cleanup SRAM */
> + bcom_sram_cleanup();
> +
> + /* Release regs */
> + iounmap(bcom->regs);
> + release_mem_region(bcom->regs_base, sizeof(struct mpc52xx_sdma));
> +
> + /* Release the node */
> + of_node_put(bcom->ofnode);
> +
> + /* Release memory */
> + kfree(bcom);
> +}
> +
> +#ifdef MODULE
> +module_init(mpc52xx_bcom_init);
> +module_exit(mpc52xx_bcom_exit);
> +#endif
> +
> +/* If we're not a module, we must make sure everything is setup
> before anyone */
> +/* tries to use us ... */
> +#ifndef MODULE
> +subsys_initcall(mpc52xx_bcom_init);
> +#endif
> +
> +MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
> +MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
> +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
> +MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
> +MODULE_LICENSE("GPL v2");
> +
> +
> +EXPORT_SYMBOL(bcom);
> +EXPORT_SYMBOL(bcom_dump_status);
> +EXPORT_SYMBOL(bcom_dump_task);
> +EXPORT_SYMBOL(bcom_dump_bdring);
> +EXPORT_SYMBOL(bcom_task_alloc);
> +EXPORT_SYMBOL(bcom_task_release);
> +EXPORT_SYMBOL(bcom_load_image);
> +EXPORT_SYMBOL(bcom_set_initiator);
> +EXPORT_SYMBOL(bcom_enable);
> +EXPORT_SYMBOL(bcom_disable);
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/
> sysdev/bestcomm/bestcomm.h
> new file mode 100644
> index 0000000..eac3eec
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h
> @@ -0,0 +1,136 @@
> +/*
> + * Public header for the MPC52xx processor BestComm driver
> + *
> + *
> + * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
> + * Copyright (C) 2005 Varma Electronics Oy,
> + * ( by Andrey Volkov <avolkov@varma-
> el.com> )
> + * Copyright (C) 2003-2004 MontaVista, Software, Inc.
> + * ( by Dale Farnsworth
> <dfarnsworth@mvista.com> )
> + *
> + * 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.
> + */
> +
> +#ifndef __BESTCOMM_H__
> +#define __BESTCOMM_H__
> +
> +struct bcom_bd; /* defined later on ... */
> +
> +
> +/*
> ======================================================================
> == */
> +/* Generic task
> managment */
> +/*
> ======================================================================
> == */
> +
> +struct bcom_task {
> + unsigned int tasknum;
> + unsigned int flags;
> + int irq;
> +
> + struct bcom_bd *bd;
> + phys_addr_t bd_pa;
> + void **cookie;
> + unsigned short index;
> + unsigned short outdex;
> + unsigned int num_bd;
> + unsigned int bd_size;
> +
> + void* priv;
> +};
> +
> +#define BCOM_FLAGS_NONE 0x00000000ul
> +#define BCOM_FLAGS_ENABLE_TASK (1ul << 0)
> +
> +
> +extern void bcom_enable(struct bcom_task *tsk);
> +extern void bcom_disable(struct bcom_task *tsk);
> +
> +static inline int
> +bcom_get_task_irq(struct bcom_task *tsk) {
> + return tsk->irq;
> +}
> +
> +
> +/* Debug dumps */
> +extern void bcom_dump_status(void);
> +extern void bcom_dump_task(int task);
> +extern void bcom_dump_bdring(struct bcom_task *tsk);
> +
> +
> +/*
> ======================================================================
> == */
> +/* BD based tasks
> helpers */
> +/*
> ======================================================================
> == */
> +
> +struct bcom_bd {
> + u32 status;
> + u32 data[1]; /* variable, but at least 1 */
> +};
> +
> +#define BCOM_BD_READY 0x40000000ul
> +
> +static inline int /* user shouldn't use this ! */
> +_bcom_next_index(struct bcom_task *tsk)
> +{
> + return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1;
> +}
> +
> +static inline int /* user shouldn't use this ! */
> +_bcom_next_outdex(struct bcom_task *tsk)
> +{
> + return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1;
> +}
> +
> +static inline int
> +bcom_queue_empty(struct bcom_task *tsk)
> +{
> + return tsk->index == tsk->outdex;
> +}
> +
> +static inline int
> +bcom_queue_full(struct bcom_task *tsk)
> +{
> + return tsk->outdex == _bcom_next_index(tsk);
> +}
> +
> +static inline int
> +bcom_buffer_done(struct bcom_task *tsk)
> +{
> + if (bcom_queue_empty(tsk))
> + return 0;
> + return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
> +}
> +
> +static inline struct bcom_bd *
> +bcom_prepare_next_buffer(struct bcom_task *tsk)
> +{
> + tsk->bd[tsk->index].status = 0; /* cleanup last status */
> + return &tsk->bd[tsk->index];
> +}
> +
> +static inline void
> +bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
> +{
> + tsk->cookie[tsk->index] = cookie;
> + mb(); /* ensure the bd is really up-to-date */
> + tsk->bd[tsk->index].status |= BCOM_BD_READY;
> + tsk->index = _bcom_next_index(tsk);
> + if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
> + bcom_enable(tsk);
> +}
> +
> +static inline void *
> +bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct
> bcom_bd **p_bd)
> +{
> + void *cookie = tsk->cookie[tsk->outdex];
> + if (p_status)
> + *p_status = tsk->bd[tsk->outdex].status;
> + if (p_bd)
> + *p_bd = &tsk->bd[tsk->outdex];
> + tsk->outdex = _bcom_next_outdex(tsk);
> + return cookie;
> +}
> +
> +
> +#endif /* __BESTCOMM_H__ */
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/
> powerpc/sysdev/bestcomm/bestcomm_priv.h
> new file mode 100644
> index 0000000..d43b00a
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
> @@ -0,0 +1,325 @@
> +/*
> + * Private header for the MPC52xx processor BestComm driver
> + *
> + *
> + * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
> + * Copyright (C) 2005 Varma Electronics Oy,
> + * ( by Andrey Volkov <avolkov@varma-
> el.com> )
> + * Copyright (C) 2003-2004 MontaVista, Software, Inc.
> + * ( by Dale Farnsworth
> <dfarnsworth@mvista.com> )
> + *
> + * 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.
> + */
> +
> +#ifndef __BESTCOMM_PRIV_H__
> +#define __BESTCOMM_PRIV_H__
> +
> +#include <linux/spinlock.h>
> +#include <asm/io.h>
> +#include <asm/prom.h>
> +#include <asm/mpc52xx.h>
> +
> +#include "sram.h"
> +
> +
> +/*
> ======================================================================
> == */
> +/* Engine related
> stuff */
> +/*
> ======================================================================
> == */
> +
> +/* Zones sizes and needed alignments */
> +#define BCOM_MAX_TASKS 16
> +#define BCOM_MAX_VAR 24
> +#define BCOM_MAX_INC 8
> +#define BCOM_MAX_FDT 64
> +#define BCOM_MAX_CTX 20
> +#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32))
> +#define BCOM_CTX_ALIGN 0x100
> +#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32))
> +#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32))
> +#define BCOM_VAR_ALIGN 0x80
> +#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32))
> +#define BCOM_FDT_ALIGN 0x100
> +
> +/* Task Descriptor Table Entry */
> +struct bcom_tdt {
> + u32 start;
> + u32 stop;
> + u32 var;
> + u32 fdt;
> + u32 exec_status; /* used internally by BestComm engine */
> + u32 mvtp; /* used internally by BestComm engine */
> + u32 context;
> + u32 litbase;
> +};
> +
> +/* This holds all info needed globaly to handle the engine */
> +struct bcom_engine {
> + struct device_node *ofnode;
> + struct mpc52xx_sdma __iomem *regs;
> + phys_addr_t regs_base;
> +
> + struct bcom_tdt *tdt;
> + u32 *ctx;
> + u32 *var;
> + u32 *fdt;
> +
> + spinlock_t lock;
> +};
> +
> +extern struct bcom_engine *bcom;
> +
> +
> +/*
> ======================================================================
> == */
> +/* Tasks related
> stuff */
> +/*
> ======================================================================
> == */
> +
> +/* Tasks image header */
> +#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */
> +
> +struct bcom_task_header {
> + u32 magic;
> + u8 desc_size; /* the size fields */
> + u8 var_size; /* are given in number */
> + u8 inc_size; /* of 32-bits words */
> + u8 first_var;
> + u8 reserved[8];
> +};
> +
> +/* Descriptors stucture & co */
> +#define BCOM_DESC_NOP 0x000001f8
> +#define BCOM_LCD_MASK 0x80000000
> +#define BCOM_DRD_EXTENDED 0x40000000
> +#define BCOM_DRD_INITIATOR_SHIFT 21
> +
> +/* Tasks pragma */
> +#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */
> +#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when
> possible, */
> + /* 1=iter end */
> +#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */
> + /* task enable */
> +#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */
> +#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */
> + /* 0=frac(msb), 1=int(lsb) */
> +#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */
> +#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */
> +#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */
> +
> + /* Looks like XLB speculative read generates XLB errors when a
> buffer
> + * is at the end of the physical memory. i.e. when accessing the
> + * lasts words, the engine tries to prefetch the next but there
> is no
> + * next ...
> + */
> +#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
> + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
> + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
> + (0 << BCOM_PRAGMA_BIT_PACK) | \
> + (0 << BCOM_PRAGMA_BIT_INTEGER) | \
> + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
> + (1 << BCOM_PRAGMA_BIT_CW) | \
> + (1 << BCOM_PRAGMA_BIT_RL))
> +
> +#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
> + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
> + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
> + (0 << BCOM_PRAGMA_BIT_PACK) | \
> + (1 << BCOM_PRAGMA_BIT_INTEGER) | \
> + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
> + (1 << BCOM_PRAGMA_BIT_CW) | \
> + (1 << BCOM_PRAGMA_BIT_RL))
> +
> +#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA
> +#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA
> +#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA
> +
> +/* Initiators number */
> +#define BCOM_INITIATOR_ALWAYS 0
> +#define BCOM_INITIATOR_SCTMR_0 1
> +#define BCOM_INITIATOR_SCTMR_1 2
> +#define BCOM_INITIATOR_FEC_RX 3
> +#define BCOM_INITIATOR_FEC_TX 4
> +#define BCOM_INITIATOR_ATA_RX 5
> +#define BCOM_INITIATOR_ATA_TX 6
> +#define BCOM_INITIATOR_SCPCI_RX 7
> +#define BCOM_INITIATOR_SCPCI_TX 8
> +#define BCOM_INITIATOR_PSC3_RX 9
> +#define BCOM_INITIATOR_PSC3_TX 10
> +#define BCOM_INITIATOR_PSC2_RX 11
> +#define BCOM_INITIATOR_PSC2_TX 12
> +#define BCOM_INITIATOR_PSC1_RX 13
> +#define BCOM_INITIATOR_PSC1_TX 14
> +#define BCOM_INITIATOR_SCTMR_2 15
> +#define BCOM_INITIATOR_SCLPC 16
> +#define BCOM_INITIATOR_PSC5_RX 17
> +#define BCOM_INITIATOR_PSC5_TX 18
> +#define BCOM_INITIATOR_PSC4_RX 19
> +#define BCOM_INITIATOR_PSC4_TX 20
> +#define BCOM_INITIATOR_I2C2_RX 21
> +#define BCOM_INITIATOR_I2C2_TX 22
> +#define BCOM_INITIATOR_I2C1_RX 23
> +#define BCOM_INITIATOR_I2C1_TX 24
> +#define BCOM_INITIATOR_PSC6_RX 25
> +#define BCOM_INITIATOR_PSC6_TX 26
> +#define BCOM_INITIATOR_IRDA_RX 25
> +#define BCOM_INITIATOR_IRDA_TX 26
> +#define BCOM_INITIATOR_SCTMR_3 27
> +#define BCOM_INITIATOR_SCTMR_4 28
> +#define BCOM_INITIATOR_SCTMR_5 29
> +#define BCOM_INITIATOR_SCTMR_6 30
> +#define BCOM_INITIATOR_SCTMR_7 31
> +
> +/* Initiators priorities */
> +#define BCOM_IPR_ALWAYS 7
> +#define BCOM_IPR_SCTMR_0 2
> +#define BCOM_IPR_SCTMR_1 2
> +#define BCOM_IPR_FEC_RX 6
> +#define BCOM_IPR_FEC_TX 5
> +#define BCOM_IPR_ATA_RX 4
> +#define BCOM_IPR_ATA_TX 3
> +#define BCOM_IPR_SCPCI_RX 2
> +#define BCOM_IPR_SCPCI_TX 2
> +#define BCOM_IPR_PSC3_RX 2
> +#define BCOM_IPR_PSC3_TX 2
> +#define BCOM_IPR_PSC2_RX 2
> +#define BCOM_IPR_PSC2_TX 2
> +#define BCOM_IPR_PSC1_RX 2
> +#define BCOM_IPR_PSC1_TX 2
> +#define BCOM_IPR_SCTMR_2 2
> +#define BCOM_IPR_SCLPC 2
> +#define BCOM_IPR_PSC5_RX 2
> +#define BCOM_IPR_PSC5_TX 2
> +#define BCOM_IPR_PSC4_RX 2
> +#define BCOM_IPR_PSC4_TX 2
> +#define BCOM_IPR_I2C2_RX 2
> +#define BCOM_IPR_I2C2_TX 2
> +#define BCOM_IPR_I2C1_RX 2
> +#define BCOM_IPR_I2C1_TX 2
> +#define BCOM_IPR_PSC6_RX 2
> +#define BCOM_IPR_PSC6_TX 2
> +#define BCOM_IPR_IRDA_RX 2
> +#define BCOM_IPR_IRDA_TX 2
> +#define BCOM_IPR_SCTMR_3 2
> +#define BCOM_IPR_SCTMR_4 2
> +#define BCOM_IPR_SCTMR_5 2
> +#define BCOM_IPR_SCTMR_6 2
> +#define BCOM_IPR_SCTMR_7 2
> +
> +
> +/*
> ======================================================================
> == */
> +/*
> API
> */
> +/*
> ======================================================================
> == */
> +
> +extern struct bcom_task *bcom_task_alloc(int bd_count, int
> bd_size, int priv_size);
> +extern void bcom_task_release(struct bcom_task *tsk);
> +
> +extern int bcom_load_image(int task, u32 *task_image);
> +extern void bcom_set_initiator(int task, int initiator);
> +
> +
> +#define TASK_ENABLE 0x8000
> +
> +static inline void
> +bcom_enable_task(int task)
> +{
> + u16 reg;
> + reg = in_be16(&bcom->regs->tcr[task]);
> + out_be16(&bcom->regs->tcr[task], reg | TASK_ENABLE);
> +}
> +
> +static inline void
> +bcom_disable_task(int task)
> +{
> + u16 reg = in_be16(&bcom->regs->tcr[task]);
> + out_be16(&bcom->regs->tcr[task], reg & ~TASK_ENABLE);
> +}
> +
> +
> +static inline u32 *
> +bcom_task_desc(int task)
> +{
> + return bcom_sram_pa2va(bcom->tdt[task].start);
> +}
> +
> +static inline int
> +bcom_task_num_descs(int task)
> +{
> + return (bcom->tdt[task].stop - bcom->tdt[task].start)/sizeof(u32)
> + 1;
> +}
> +
> +static inline u32 *
> +bcom_task_var(int task)
> +{
> + return bcom_sram_pa2va(bcom->tdt[task].var);
> +}
> +
> +static inline u32 *
> +bcom_task_inc(int task)
> +{
> + return &bcom_task_var(task)[BCOM_MAX_VAR];
> +}
> +
> +
> +static inline int
> +bcom_drd_is_extended(u32 desc)
> +{
> + return (desc) & BCOM_DRD_EXTENDED;
> +}
> +
> +static inline int
> +bcom_desc_is_drd(u32 desc)
> +{
> + return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP;
> +}
> +
> +static inline int
> +bcom_desc_initiator(u32 desc)
> +{
> + return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f;
> +}
> +
> +static inline void
> +bcom_set_desc_initiator(u32 *desc, int initiator)
> +{
> + *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) |
> + ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT);
> +}
> +
> +
> +static inline void
> +bcom_set_task_pragma(int task, int pragma)
> +{
> + u32 *fdt = &bcom->tdt[task].fdt;
> + *fdt = (*fdt & ~0xff) | pragma;
> +}
> +
> +static inline void
> +bcom_set_task_auto_start(int task, int next_task)
> +{
> + u16 __iomem *tcr = &bcom->regs->tcr[task];
> + out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task);
> +}
> +
> +static inline void
> +bcom_set_tcr_initiator(int task, int initiator)
> +{
> + u16 __iomem *tcr = &bcom->regs->tcr[task];
> + out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8));
> +}
> +
> +
> +#endif /* __BESTCOMM_PRIV_H__ */
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/
> sysdev/bestcomm/sram.c
> new file mode 100644
> index 0000000..4f69127
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/sram.c
> @@ -0,0 +1,180 @@
> +/*
> + * Simple memory allocator for on-board SRAM
> + *
> + *
> + * Maintainer : Sylvain Munaut <tnt@246tNt.com>
> + *
> + * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/ioport.h>
> +
> +#include <asm/io.h>
> +#include <asm/mmu.h>
> +#include <asm/prom.h>
> +
> +#include "sram.h"
> +
> +
> +/* Struct keeping our 'state' */
> +struct bcom_sram *bcom_sram = NULL;
> +
> +
> +/*
> ======================================================================
> == */
> +/* Public
> API */
> +/*
> ======================================================================
> == */
> +/* DO NOT USE in interrupts, if needed in irq handler, we should
> use the
> + _irqsave version of the spin_locks */
> +
> +int bcom_sram_init(struct device_node *sram_node, char *owner)
> +{
> + int rv;
> + const u32 *regaddr_p;
> + u64 regaddr64, size64;
> + unsigned int psize;
> +
> + /* Create our state struct */
> + if (bcom_sram) {
> + printk(KERN_ERR "%s: bcom_sram_init: "
> + "Already initialiwed !\n", owner);
> + return -EBUSY;
> + }
> +
> + bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL);
> + if (!bcom_sram) {
> + printk(KERN_ERR "%s: bcom_sram_init: "
> + "Couldn't allocate internal state !\n", owner);
> + return -ENOMEM;
> + }
> +
> + /* Get address and size of the sram */
> + regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
> + if (!regaddr_p) {
> + printk(KERN_ERR "%s: bcom_sram_init: "
> + "Invalid device node !\n", owner);
> + rv = -EINVAL;
> + goto error_free;
> + }
> +
> + regaddr64 = of_translate_address(sram_node, regaddr_p);
> +
> + bcom_sram->base_phys = (phys_addr_t) regaddr64;
> + bcom_sram->size = (unsigned int) size64;
> +
> + /* Request region */
> + if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size,
> owner)) {
> + printk(KERN_ERR "%s: bcom_sram_init: "
> + "Couln't request region !\n", owner);
> + rv = -EBUSY;
> + goto error_free;
> + }
> +
> + /* Map SRAM */
> + /* sram is not really __iomem */
> + bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys,
> bcom_sram->size);
> +
> + if (!bcom_sram->base_virt) {
> + printk(KERN_ERR "%s: bcom_sram_init: "
> + "Map error SRAM zone 0x%08lx (0x%0x)!\n",
> + owner, bcom_sram->base_phys, bcom_sram->size );
> + rv = -ENOMEM;
> + goto error_release;
> + }
> +
> + /* Create an rheap (defaults to 32 bits word alignment) */
> + bcom_sram->rh = rh_create(4);
> +
> + /* Attach the free zones */
> +#if 0
> + /* Currently disabled ... for future use only */
> + reg_addr_p = of_get_property(sram_node, "available", &psize);
> +#else
> + regaddr_p = NULL;
> + psize = 0;
> +#endif
> +
> + if (!regaddr_p || !psize) {
> + /* Attach the whole zone */
> + rh_attach_region(bcom_sram->rh, 0, bcom_sram->size);
> + } else {
> + /* Attach each zone independently */
> + while (psize >= 2 * sizeof(u32)) {
> + phys_addr_t zbase = of_translate_address(sram_node, regaddr_p);
> + rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys,
> regaddr_p[1]);
> + regaddr_p += 2;
> + psize -= 2 * sizeof(u32);
> + }
> + }
> +
> + /* Init our spinlock */
> + spin_lock_init(&bcom_sram->lock);
> +
> + return 0;
> +
> +error_release:
> + release_mem_region(bcom_sram->base_phys, bcom_sram->size);
> +error_free:
> + kfree(bcom_sram);
> + bcom_sram = NULL;
> +
> + return rv;
> +}
> +
> +void bcom_sram_cleanup(void)
> +{
> + /* Free resources */
> + if (bcom_sram) {
> + rh_destroy(bcom_sram->rh);
> + iounmap((void __iomem *)bcom_sram->base_virt);
> + release_mem_region(bcom_sram->base_phys, bcom_sram->size);
> + kfree(bcom_sram);
> + bcom_sram = NULL;
> + }
> +}
> +
> +void* bcom_sram_alloc(int size, int align, phys_addr_t *phys)
> +{
> + unsigned long offset;
> +
> + spin_lock(&bcom_sram->lock);
> + offset = rh_alloc_align(bcom_sram->rh, size, align, NULL);
> + spin_unlock(&bcom_sram->lock);
> +
> + if (IS_ERR_VALUE(offset))
> + return NULL;
> +
> + *phys = bcom_sram->base_phys + offset;
> + return bcom_sram->base_virt + offset;
> +}
> +
> +void bcom_sram_free(void *ptr)
> +{
> + unsigned long offset;
> +
> + if (!ptr)
> + return;
> +
> + offset = ptr - bcom_sram->base_virt;
> +
> + spin_lock(&bcom_sram->lock);
> + rh_free(bcom_sram->rh, offset);
> + spin_unlock(&bcom_sram->lock);
> +}
> +
> +
> +EXPORT_SYMBOL(bcom_sram);
> +
> +EXPORT_SYMBOL(bcom_sram_init);
> +EXPORT_SYMBOL(bcom_sram_cleanup);
> +EXPORT_SYMBOL(bcom_sram_alloc);
> +EXPORT_SYMBOL(bcom_sram_free);
> +
> diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/arch/powerpc/
> sysdev/bestcomm/sram.h
> new file mode 100644
> index 0000000..b6d6689
> --- /dev/null
> +++ b/arch/powerpc/sysdev/bestcomm/sram.h
> @@ -0,0 +1,54 @@
> +/*
> + * Handling of a sram zone for bestcomm
> + *
> + *
> + * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
> + *
> + * 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.
> + */
> +
> +#ifndef __BESTCOMM_SRAM_H__
> +#define __BESTCOMM_SRAM_H__
> +
> +#include <asm/rheap.h>
> +#include <asm/mmu.h>
> +#include <linux/spinlock.h>
> +
> +
> +/* Structure used internally */
> + /* The internals are here for the inline functions
> + * sake, certainly not for the user to mess with !
> + */
> +struct bcom_sram {
> + phys_addr_t base_phys;
> + void *base_virt;
> + unsigned int size;
> + rh_info_t *rh;
> + spinlock_t lock;
> +};
> +
> +extern struct bcom_sram *bcom_sram;
> +
> +
> +/* Public API */
> +extern int bcom_sram_init(struct device_node *sram_node, char
> *owner);
> +extern void bcom_sram_cleanup(void);
> +
> +extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys);
> +extern void bcom_sram_free(void *ptr);
> +
> +static inline phys_addr_t bcom_sram_va2pa(void *va) {
> + return bcom_sram->base_phys +
> + (unsigned long)(va - bcom_sram->base_virt);
> +}
> +
> +static inline void *bcom_sram_pa2va(phys_addr_t pa) {
> + return bcom_sram->base_virt +
> + (unsigned long)(pa - bcom_sram->base_phys);
> +}
> +
> +
> +#endif /* __BESTCOMM_SRAM_H__ */
> +
> --
> 1.5.1.2
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200
2007-05-15 21:37 ` Kumar Gala
@ 2007-05-15 22:27 ` Sylvain Munaut
0 siblings, 0 replies; 24+ messages in thread
From: Sylvain Munaut @ 2007-05-15 22:27 UTC (permalink / raw)
To: Kumar Gala; +Cc: Linux PPC dev ML
Kumar Gala wrote:
>
> On May 12, 2007, at 3:31 PM, Sylvain Munaut wrote:
>
>> This patch adds support for the core of the BestComm API
>> for the Freescale MPC5200(b). The BestComm engine is a
>> microcode-controlled / tasks-based DMA used by several
>> of the onchip devices.
>>
>> Setting up the tasks / memory allocation and all common
>> low level functions are handled by this patch.
>> The specifics details of each tasks and their microcode
>> are split-out in separate patches.
>>
>> This is not the official API, but a much cleaner one.
>
> Can you give more detail about how the API works.
Yes, I'll try to write something that gives a little explanation for
Documentation/
In the mean time, here's a small informative text that
describe the API usage from a driver writer perspective :
You need to allocate yourself a task, depending on
what driver you're writing, that might be a generic
one or one dedicated to that device (depends on the
peripheral). For example FEC needs one TX and one RX
task, both specialized.
Each 'type' of task you can create has a specific
function to create it. For example, to create a new
instance of the FEC rx task, you call bcom_fec_rx_init.
It returns a (struct bcom_task*) that's gonna be your link
to bestcomm for all further calls.
Each task "type" needs a different init function because
depending on the microcode, the way to initialize it
is slightly different ...
Most task are buffer descriptor based (well ... currently,
they all are but that's not an obligation), and the
bestcomm API handle details of the BD ring commonly.
It assumes each buffer descriptor is formed by a
status word and a certain number of data pointers (1,2,..n).
The signification of each of theses data pointers and
of the status depends on the type of task. There is however
a convention in all tasks that the upper 4 bits of the status
are reserved for "Ready" flags and that the lsbs are for
the length (how much lsb depends of task ...)
When you need to submit a new buffer for sending, you just
call bcom_prepare_next_buffer(...) that returns you a pointer
to a (struct bcom_bd *) to fill up with the info you want
(most often just the lenght and data pointers ...).
When filled up, you just "push" the BD calling
bcom_submit_next_buffer(...). Each time a BD is completed,
bestcomm generates an interrupt and you just have to
call bcom_retrieve_buffer(...) to reclaim the BD that was
sent (along with the info needed to identify which one it is).
For receive task, it's more or less the same except you
submit empty buffer that bestcomm will fill up. And the
status word in the bd you recover by calling _retrieve is
gonna give you how many bytes were put into the buffer.
>
>> diff --git a/arch/powerpc/sysdev/bestcomm/Makefile
>> b/arch/powerpc/sysdev/bestcomm/Makefile
>> new file mode 100644
>> index 0000000..a24aa06
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/bestcomm/Makefile
>> @@ -0,0 +1,8 @@
>> +#
>> +# Makefile for BestComm & co
>> +#
>> +
>> +bestcomm-core-objs := bestcomm.o sram.o
>> +
>> +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
>
> Any reason why sram isn't on the ojb-$(CONFIG_PPC_BESTCOMM) line?
Huh, I want the module to be named bestcomm-core when built as modules,
is it possible to do it in another way ?
I'm not an expert as kbuild so if there is a better way ...
>>
>> +/* Debug Dump */
>> +
>> +#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a,
>> ## b)
>
> We have dev_dbg and dev_printk can we not use them?
I don't use dev_dbg because I want the stuff printed even if DEBUG was
not defined when
compiling bestcomm-core.
Those are debug functions that a developer can call, from the driver, to
see what state bestcomm is
in. So when he calls them, the user expects them to print stuff,
regardless is DEBUG was defined
or not when compiling bestcomm-core ...
Same thing for pr_debug.
And dev_printk forces me to repeat "dev_printk(dev, KERN_DEBUG, " on
each line which makes
them very long ...
I could define BCOM_DPRINTK(a,b,...) as dev_printk(dev, KERN_DEBUG, ...)
if you think it's better.
>
> Would all bcom_dump_status(), bcom_dump_task(), bcom_dump_bdring be
> better using debugfs? At minimum there should be a Kconfig option to
> enable bestcomm debug that enables this code.
Theses are _never_ called ...
They are just there because when you work on a driver and something goes
wrong, it's useful to print debug info to see exactly what happened at
that exact moment.
So for example when working on the sound driver, some times I got an
interrupt but no buffer was finished, printing the bestcomm status at
that exact moment in the ISR when detecting that condition allowed me to
figure out what was going on. On the final submitted driver the call
will be removed ...
>> +/* Private API */
>> +
>
> What's private about it?
Driver code should not use it directly.
Only task support code should use it.
(basically anything outside sysdev/bestcomm/* should not include
bestcomm_priv.h)
> It would probably be good for the API functions to have DocBook style
> comments. See something like drivers/rapidio/rio.c for an example.
Yes that would be nice ... just need to write it ;)
>>
>> +void
>> +bcom_task_release(struct bcom_task *tsk)
> bcom_task_free() to match alloc/free semantics?
Ok, I'll change that.
>> +
>> +
>> +/* Public API */
>> +
> What's public about these?
Driver can use those ;)
Thanks for the comments.
Sylvain
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2007-05-15 22:27 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-12 20:31 [PATCH 0/9] BestComm : better late than never heh ;) Sylvain Munaut
2007-05-12 20:31 ` [PATCH 1/9] powerpc: exports rheap symbol to modules Sylvain Munaut
2007-05-12 20:31 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Sylvain Munaut
2007-05-12 20:31 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Sylvain Munaut
2007-05-12 20:31 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Sylvain Munaut
2007-05-12 20:31 ` [PATCH 5/9] powerpc: BestcComm ATA task support Sylvain Munaut
2007-05-12 20:31 ` [PATCH 6/9] powerpc: BestcComm FEC " Sylvain Munaut
2007-05-12 20:31 ` [PATCH 7/9] powerpc: BestcComm GenBD " Sylvain Munaut
2007-05-12 20:31 ` [PATCH 8/9] drivers/net: Add support for Freescale MPC5200 SoC internal FEC Sylvain Munaut
2007-05-12 20:31 ` [PATCH 9/9] sound: Add support for Freescale MPC5200 AC97 interface Sylvain Munaut
2007-05-12 23:30 ` [PATCH 5/9] powerpc: BestcComm ATA task support Arnd Bergmann
2007-05-12 23:27 ` [PATCH 4/9] powerpc: BestComm core support for Freescale MPC5200 Arnd Bergmann
2007-05-12 23:49 ` Sylvain Munaut
2007-05-13 0:24 ` Arnd Bergmann
2007-05-13 7:17 ` Sylvain Munaut
2007-05-13 23:29 ` Matt Sealey
2007-05-14 5:15 ` Sylvain Munaut
2007-05-13 3:36 ` Dale Farnsworth
2007-05-15 21:37 ` Kumar Gala
2007-05-15 22:27 ` Sylvain Munaut
2007-05-13 23:46 ` [PATCH 3/9] powerpc/ppc32: Update mpc52xx_psc structure with B revision changes Matt Sealey
2007-05-14 5:27 ` Sylvain Munaut
2007-05-15 10:59 ` Raquel Velasco and Bill Buck
2007-05-15 21:20 ` [PATCH 2/9] powerpc: Changes the config mechanism for rheap Kumar Gala
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).