All of lore.kernel.org
 help / color / mirror / Atom feed
* Bestcomm tasks and interrupts on MPC5200(B)
@ 2009-01-20 13:15 Dave Best
  2009-01-20 15:51 ` Frank Bennett
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Dave Best @ 2009-01-20 13:15 UTC (permalink / raw)
  To: linuxppc-dev

I'm trying to write a driver which uses the Local Plus Bus on my MPC5200B a=
nd therefore have to use BestComm DMA, which requires me to use a Gen_BD ta=
sk for data transfer with Local Plus.
I tried to follow the fec driver that is currently used and took a peek at =
the mpc52xx-ac97 driver which at least uses the same kind of bus as I.

Initialising the task, resetting and enabling works fine. Even request_irq =
reports no error, but when I start a transfer it hangs and if I am lucky, a=
n interrupt occurs after quite some time. But it's always the BestComm ethe=
rnet rx task which produces an RFIFO interrupt, presumably after the watchd=
og catches on.=20
If this happens my interrupt occurs to.

I tried to debug this situation but I am still clueless.
If I use the MPC5200 Interrupt emulation registers to force an interrupt fo=
r my interface to occur, nothing happens except that it hangs.

Any hints, tips or help appreciated.

Dave=0A=0A=0A      

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-20 13:15 Bestcomm tasks and interrupts on MPC5200(B) Dave Best
@ 2009-01-20 15:51 ` Frank Bennett
  2009-01-20 15:59 ` Frank Bennett
  2009-01-20 16:04 ` Grant Likely
  2 siblings, 0 replies; 7+ messages in thread
From: Frank Bennett @ 2009-01-20 15:51 UTC (permalink / raw)
  To: arieswar24b; +Cc: linuxppc-dev


[-- Attachment #1.1: Type: text/plain, Size: 1248 bytes --]

Dave Best wrote:
> I'm trying to write a driver which uses the Local Plus Bus on my MPC5200B and therefore have to use BestComm DMA, which requires me to use a Gen_BD task for data transfer with Local Plus.
> I tried to follow the fec driver that is currently used and took a peek at the mpc52xx-ac97 driver which at least uses the same kind of bus as I.
>   
Find attached a Bestcomm instruction set summary sheet from the 
Freescale folks.
Hope this helps.

-Frank
> Initialising the task, resetting and enabling works fine. Even request_irq reports no error, but when I start a transfer it hangs and if I am lucky, an interrupt occurs after quite some time. But it's always the BestComm ethernet rx task which produces an RFIFO interrupt, presumably after the watchdog catches on. 
> If this happens my interrupt occurs to.
>
> I tried to debug this situation but I am still clueless.
> If I use the MPC5200 Interrupt emulation registers to force an interrupt for my interface to occur, nothing happens except that it hangs.
>
> Any hints, tips or help appreciated.
>
> Dave
>      
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>   



[-- Attachment #1.2: Type: text/html, Size: 2081 bytes --]

[-- Attachment #2: sdHandAssemblyLcdDrd.pdf --]
[-- Type: application/pdf, Size: 9985 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-20 13:15 Bestcomm tasks and interrupts on MPC5200(B) Dave Best
  2009-01-20 15:51 ` Frank Bennett
@ 2009-01-20 15:59 ` Frank Bennett
  2009-01-20 16:04 ` Grant Likely
  2 siblings, 0 replies; 7+ messages in thread
From: Frank Bennett @ 2009-01-20 15:59 UTC (permalink / raw)
  To: arieswar24b; +Cc: linuxppc-dev

Dave Best wrote:
> I'm trying to write a driver which uses the Local Plus Bus on my MPC5200B and therefore have to use BestComm DMA, which requires me to use a Gen_BD task for data transfer with Local Plus.
> I tried to follow the fec driver that is currently used and took a peek at the mpc52xx-ac97 driver which at least uses the same kind of bus as I.
>
> Initialising the task, resetting and enabling works fine. Even request_irq reports no error, but when I start a transfer it hangs and if I am lucky, an interrupt occurs after quite some time. But it's always the BestComm ethernet rx task which produces an RFIFO interrupt, presumably after the watchdog catches on. 
> If this happens my interrupt occurs to.
>
> I tried to debug this situation but I am still clueless.
> If I use the MPC5200 Interrupt emulation registers to force an interrupt for my interface to occur, nothing happens except that it hangs.
>
> Any hints, tips or help appreciated.
>   
Below is a Disassembler I wrote a couple years ago. Just paste
the hex of interest in the array below, re-compile and run.

Cheers,
-Frank
> Dave
>
>
>       
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>   

/*
 * disasm.c - disassembler for MPC5200 Bestcomm DMA Firmware
 *            copy and paste task code into fw, compile a run
 * by Frank bennett, 3/29/2006
 *
 * Based on Freescale pdf "SmartDMA Hand-Assembly Guides"
 *
 * TODO:
 *  o add inc[0:7] array (maybe var) to deternmine proper term condition (upper 3 bits)
 *  o need to review sheet 3 of the pdf
 *  o simulator would be nice
 *
 */
// Task12 (TASK_GEN_TX_BD) : Start of TDT -> 0xf0008528
// linuxppc_2_4_devel/arch/ppc/5xxx_io/bestcomm/code_dma/image_rtos1/"dma_image.reloc.c
//
// 31,30,29,28|27,26,25,24|23,22,21,20|19,18,17,16|15,14,13,12|11,10, 9, 8| 7, 6, 5, 4| 3, 2, 1, 0
//  0  0  0  1  0  0| 0  0  0  0  0| 0  0| 0  0| 0| 0  0  0  1  0  0| 1| 1  0  0  0  0  1| 0  0  0
// 0x10001308, /* 000C      DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=2 */
//  1  0  0| 1  1  0  0  1  0| 0| 0| 1  1  0  0  1  0| 0  0| 0| 0  0  0  0  0  0| 1  1  0| 1  1  0
// 0x99190036, /* 002C    LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */
// 31,30,29,28|27,26,25,24|23,22,21,20|19,18,17,16|15,14,13,12|11,10, 9, 8| 7, 6, 5, 4| 3, 2, 1, 0
// 1   0| 0  1  1  0, 0  1  0  1,    1  0  0, 0  0  1| 1  0| 1  0  0  1  0  0  0  0  1  0  0  0  0
// 0x9950d210

/* Task12(TASK_GEN_TX_BD): Start of TDT -> 0xf0008528 */
unsigned long fw[] = {
    0x800220e3, /* 0000  LCD: idx0 = var0, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */
    0x13e01010, /* 0004    DRD1A: var4 = var2; FN=0 MORE init=24 WS=0 RS=0 */
    0xb8808264, /* 0008    LCD: idx2 = *idx1, idx3 = var1; idx2 < var9; idx2 += inc4, idx3 += inc4 */
    0x10001308, /* 000C      DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=2 */
    0x60140002, /* 0010      DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
    0x0cccfcca, /* 0014      DRD2B1: *idx3 = EU3(); EU3(*idx3,var10)  */
    0xd9190300, /* 0018    LCDEXT: idx2 = idx2; idx2 > var12; idx2 += inc0 */
    0xb8c5e009, /* 001C    LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */
    0x03fec398, /* 0020      DRD1A: *idx0 = *idx3; FN=0 init=24 WS=3 RS=2 */
    0x9919826a, /* 0024    LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc5, idx3 += inc2 */
    0x0feac398, /* 0028      DRD1A: *idx0 = *idx3; FN=0 TFD INT init=24 WS=1 RS=2 */
    0x99190036, /* 002C    LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */
    0x60000005, /* 0030      DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
    0x0c4cf889, /* 0034      DRD2B1: *idx1 = EU3(); EU3(idx2,var9)  */
    0x000001f8, /* 0038    NOP */
    0x9950d210,
	0x2c4cf889,
    0
};

union
{
    unsigned long i;
    struct
    {
        unsigned sb:3;     // [02:00] increment #2
        unsigned sa:3;     // [05:03] increment #1
        unsigned tc:6;     // [11:06] variable to which idx is compared   
        unsigned drtc:1;   //    [12] dr ? *(tc) : (tc)
        unsigned tu:2;     // [14:13] term usage 00-idx_a, 01-idx_b, 10- lit init 11-no cond
        unsigned ib:6;     // [20:15] init_b 
        unsigned drib:1;   //    [21] dr ? *(init_a) : (init_a)
        unsigned p:1;      //    [22] indx plus offset
        unsigned ia:6;     // [28:23] init_a 
        unsigned dria:1;   //    [29] dr ? *(init_a) : (init_a)
        unsigned ext:1;    //    [30] = 2 or 3 for LCD
        unsigned op:1;     //    [31] = 2 or 3 for LCD
    } lcd;
    struct
    {
        unsigned ll:13;    // [12:00] literal init low
        unsigned tu:2;     // [14:13] term usage == 2
        unsigned lh:15;    // [29:15] literal init hi 
        unsigned bas:1;    //    [30] = 2 or 3 for LCD
        unsigned op:1;     //    [31] = 2 or 3 for LCD
    } lcdl;

    struct
    {
        unsigned fn:3;     // [02:00]
        unsigned src:6;    // [08:03]
        unsigned drs:1;    //    [09] deref src?
        unsigned dst:6;    // [15:10]
        unsigned drd:1;    //    [16] deref dst? scal?
        unsigned ws:2;     // [18:17] 0-32bit, 1-1 byte, 2-16 word, 3-dynamic/reserved??
        unsigned rs:2;     // [20:19] 0-32bit, 1-1 byte, 2-16 word, 3-dynamic/reserved??
        unsigned init:5;   // [25:21]
        unsigned intr:1;   //    [26]
        unsigned tfd:1;    //    [27]
        unsigned more:1;   //    [28]      
        unsigned type:1;   //    [29] 
        unsigned ext:1;    //    [30] 
        unsigned op:1;     //    [31] = 0 for DRD
    } drd1a;
    struct
    {
        unsigned eu3:4;    // [03:00] Execution Unit3 Function number
        unsigned eu2:4;    // [07:04] Execution Unit3 Function number
        unsigned eu1:4;    // [11:08] Execution Unit3 Function number
        unsigned eu0:4;    // [15:12] Execution Unit3 Function number
        unsigned drd:1;    //    [16] deref dst? scal?
        unsigned ws:2;     // [18:17] 0-32bit, 1-1 byte, 2-16 word, 3-dynamic/reserved??
        unsigned rs:2;     // [20:19] 0-32bit, 1-1 byte, 2-16 word, 3-dynamic/reserved??
        unsigned init:5;   // [25:21]
        unsigned int:1;    //    [26]
        unsigned tfd:1;    //    [27]
        unsigned more:1;   //    [28]      
        unsigned type:1;   //    [29] 
        unsigned ext:1;    //    [30] 
        unsigned op:1;     //    [31] = 0 for DRD
    } drd2a;
    struct
    {
        unsigned od1:6;    // [05:00] EU operand 1
        unsigned od0:6;    // [11:06] EU operand 0
        unsigned eu:2;     // [13:12] EU#, only #3 available on MPC5200
        unsigned mems:6;   // [19:14] memory write source
        unsigned drds:1;   //    [20]      
        unsigned rsv2:1;   //    [21]      
        unsigned memd:6;   // [27:22] memory write destination
        unsigned rsv1:1;   //    [28] reserved?
        unsigned type:1;   //    [29] 
        unsigned ext:1;    //    [30] 
        unsigned op:1;     //    [31] = 0 for DRD
    } drd2b1;
    struct
    {
        unsigned odb1:6;   // [05:00] EUb operand 1/3/5/7
        unsigned odb0:6;   // [11:06] EUb operand 0/2/4/6
        unsigned eub:2;    // [13:12] EUb#, only #3 available on MPC5200
        unsigned oda1:6;   // [19:14] EUa operand 1/3/5/7
        unsigned oda0:6;   // [25:20] EUa operand 0/2/4/6
        unsigned eua:2;    // [27:26] EUa#, only #3 available on MPC5200
        unsigned rsv1:1;   //    [28]      
        unsigned type:1;   //    [29] 
        unsigned ext:1;    //    [30] 
        unsigned op:1;     //    [31] = 0 for DRD
    } drd2b2;
} x;

// Condition codes (first 4 bits of the increments)
// >=   c
// >    4
// once 0
// !=   6
// <=   a
// <    2

char *ft[] = {
    "ld_acc", "unld_acc", "and", "or", "xor" "andn", "not",
    "add", "sub", "lsh", "rsh", "crc8", "crc16", "crc32", 
	"endian32", "endian16"
};

char s[8];
indvar (int i)
{
    switch ((i & 0x3f) >> 4)
    {
    case 0:
        sprintf (s, "var%d", i & 0x1f); // varN
        break;
    case 1:
        sprintf (s, "inc%d", i & 0x07); // incN
        break;
    case 2:    			                // extraN
    case 3:
        sprintf (s, "idx%d", i & 0x0f); // idxN
        break;
    }
}

#define L2 LLev*2
main ()
{
    int i, j, LLev = 0, drdt, ex = 0, ml;
    char in[12];

    for (i = 0; fw[i]; i++)
    {
        x.i = fw[i];
        if (x.i == 0x1f8)
            printf ("0x%08x, // NOP\n", x.i);
        else if (x.lcd.op)      	// LCD
        {
            if (LLev == 0)
                strcpy (in, "");
            else
                strcpy (in, "  ");

            if (x.lcd.ext)			// LCDEXT
            {
                indvar (x.lcd.tc);
                printf
                    ("0x%08x, // %sLCDEXT: idx%d=idx%d; idx%d >%s%s ; idx%d += inc%d\n",
                     x.i, in, L2, L2, L2, x.lcd.drtc ? "*" : " ", s, L2,
                     x.lcd.sb);
            } else if (x.lcd.tu == 2)	 // LCDLIT
            {
                printf ("0x%08x, // %sLCDLIT: idx%d = 0x%07x ??\n",
                        x.i, in, L2, (x.lcdl.lh << 13) | x.lcdl.ll);
            } else                	// LCD
            {
                printf ("0x%08x, // %sLCD: ", x.i, in);
                if (x.lcd.p)    // indexed p-plus
                {
                    indvar (x.lcd.ia);
                    printf ("idx%d = %s(%s + ", L2 + 1, x.lcd.dria ? "*" : " ", s);
                    indvar (x.lcd.ib);
                    printf ("%s%s); ; ", x.lcd.drib ? "*" : " ", s);
                } else
                {
                    indvar (x.lcd.ia);
                    printf ("idx%d=%s%s, ", L2, x.lcd.dria ? "*" : " ", s);
                    indvar (x.lcd.ib);
                    printf ("idx%d=%s%s, ", L2 + 1, x.lcd.drib ? "*" : " ", s);
                }
                if (x.lcd.tu < 2)
                {
                    indvar (x.lcd.tc);
                    printf ("idx%d <=%s%s, ", (x.lcd.tu == 0) ? L2 : L2 + 1,
                            x.lcd.drtc ? "*" : " ", s);
                }
                if (x.lcd.tu != 3)
                    printf ("idx%d += inc%01x, ", L2, x.lcd.sa);
                printf ("idx%d += inc%01x\n", L2 + 1, x.lcd.sb);
                ex = 0;
            }
        }
        else
        {
            // DRD
            if (LLev == 0)
                strcpy (in, "  ");
            else
                strcpy (in, "    ");
            drdt = (ex << 2) | (x.drd1a.ext << 1) | x.drd1a.type;
            switch (drdt)
            {
            case 0:            // DRD1A
                printf ("0x%08x, // %sDRD1A: ", x.i, in);
                indvar (x.drd1a.dst);
                printf ("%s%s =", x.drd1a.drd ? "*" : " ", s);
                indvar (x.drd1a.src);
                printf ("%s%s; ", x.drd1a.drs ? "*" : " ", s);
                printf ("FN=%d (%s) ", x.drd1a.fn, ft[x.drd1a.fn]);
                if (x.drd1a.more)
                    printf ("MORE ");
                if (x.drd1a.tfd)
                    printf ("TFD ");
                if (x.drd1a.intr)
                    printf ("INT ");
                printf ("init=%d ", x.drd1a.init);
                printf ("WS=%d RS=%d \n", x.drd1a.ws, x.drd1a.rs);
                break;
            case 3:            // DRD2A
                ex = x.drd2a.ext;
                printf ("0x%08x, // %sDRD2A: ", x.i, in);
                printf ("EU0=0 EU1=0 EU2=0 EU3=%d (%s) EXT init=%d ",
                        x.drd2a.eu3, ft[x.drd2a.eu3], x.drd2a.init);
                printf ("WS=%d RS=%d \n", x.drd2a.ws, x.drd2a.rs);
                break;
            case 4:            // DRD2B1
                printf ("0x%08x, // %sDRD2B1: ", x.i, in);
                indvar (x.drd2b1.memd);   // mems ??
                printf ("*%s=EU%d(); ", s, x.drd2b1.eu);
                indvar (x.drd2b1.od0); printf ("EU%d(%s, ", x.drd2b1.eu, s);
                indvar (x.drd2b1.od1); printf ("%s) \n", s);
                break;
            case 5:            // DRD2B2
            case 7:            // DRD2B2?
                printf ("0x%08x, // %sDRD2B2: ", x.i, in);
                indvar (x.drd2b2.oda0); printf ("EU%d(%s, ", x.drd2b2.eub, s);
                indvar (x.drd2b2.oda1); printf ("%s) , ", s);
                indvar (x.drd2b2.odb0); printf ("EU%d(%s, ", x.drd2b2.eua, s);
                indvar (x.drd2b2.odb1); printf ("%s) ??\n", s);
				break;
            case 1:
            case 2:
            case 6:
                printf ("fix_me\n");
                ex = x.drd1a.ext;
                break;
            }
            LLev |= 1;
        }
    }
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-20 13:15 Bestcomm tasks and interrupts on MPC5200(B) Dave Best
  2009-01-20 15:51 ` Frank Bennett
  2009-01-20 15:59 ` Frank Bennett
@ 2009-01-20 16:04 ` Grant Likely
  2009-01-21 12:57   ` Dave Best
  2 siblings, 1 reply; 7+ messages in thread
From: Grant Likely @ 2009-01-20 16:04 UTC (permalink / raw)
  To: arieswar24b; +Cc: linuxppc-dev

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

On Tue, Jan 20, 2009 at 6:15 AM, Dave Best <arieswar24b@yahoo.de> wrote:
> I'm trying to write a driver which uses the Local Plus Bus on my MPC5200B and therefore have to use BestComm DMA, which requires me to use a Gen_BD task for data transfer with Local Plus.
> I tried to follow the fec driver that is currently used and took a peek at the mpc52xx-ac97 driver which at least uses the same kind of bus as I.
>
> Initialising the task, resetting and enabling works fine. Even request_irq reports no error, but when I start a transfer it hangs and if I am lucky, an interrupt occurs after quite some time. But it's always the BestComm ethernet rx task which produces an RFIFO interrupt, presumably after the watchdog catches on.
> If this happens my interrupt occurs to.

Are you using the LocalPlus fifo device for the transfer (you need to
if you aren't)?

I've attached a test driver that demonstrates how to do FIFO only and
FIFO+DMA transfers over the localplus bus.

g.




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

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Bestcomm-localplus-test-utility.patch --]
[-- Type: text/x-patch; name=0001-Add-Bestcomm-localplus-test-utility.patch, Size: 27962 bytes --]

From 23ca0c4b1fa01ace41720aaa0fb32bd4351d0afc Mon Sep 17 00:00:00 2001
From: Grant Likely <grant.likely@secretlab.ca>
Date: Mon, 5 Jan 2009 00:53:51 -0700
Subject: [PATCH] Add Bestcomm/localplus test utility

---
 drivers/misc/Kconfig                  |    4 +
 drivers/misc/Makefile                 |    1 +
 drivers/misc/mpc5200-localplus-test.c |  937 +++++++++++++++++++++++++++++++++
 3 files changed, 942 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/mpc5200-localplus-test.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index fee7304..edcab03 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -13,6 +13,10 @@ menuconfig MISC_DEVICES
 
 if MISC_DEVICES
 
+config MPC5200_LOCALPLUS_PERF_TEST
+	tristate "MPC5200 LocalPlus Bus performance test module"
+	select PPC_BESTCOMM_GEN_BD
+
 config ATMEL_PWM
 	tristate "Atmel AT32/AT91 PWM support"
 	depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 817f7f5..19a3d92 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -33,3 +33,4 @@ obj-$(CONFIG_SGI_XP)		+= sgi-xp/
 obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
 obj-$(CONFIG_HP_ILO)		+= hpilo.o
 obj-$(CONFIG_C2PORT)		+= c2port/
+obj-$(CONFIG_MPC5200_LOCALPLUS_PERF_TEST) += mpc5200-localplus-test.o
diff --git a/drivers/misc/mpc5200-localplus-test.c b/drivers/misc/mpc5200-localplus-test.c
new file mode 100644
index 0000000..8ba98fc
--- /dev/null
+++ b/drivers/misc/mpc5200-localplus-test.c
@@ -0,0 +1,937 @@
+/*
+ * LocalPlusBus performance tests.
+ *
+ * Copyright (C) Secret Lab Technologies Ltd. 2008-2009
+ *
+ * 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.
+ *
+ * This file implements a set of LocalPlus bus performance tests when using
+ * direct Programmed IO (PIO), the LocalPlus FIFO, and when using the
+ * Bestcomm DMA engine to transfer data.  It can be compiled into the
+ * kernel or loaded as a module.
+ *
+ * The test module is controlled via files in the sysfs filesystem.  Special
+ * control files are created in /sys/devices/platform/lpbtest.0 which
+ * control the tests and report the results.  Test parameters are set by
+ * writing values into the parameter files (blocksize, blockcount, period,
+ * and type).  The test is started and stopped with the 'action' file.
+ * Results are retrieved by reading the contents of the 'results' file.
+ *
+ * The following parameters can be modified:
+ * blocksize: number of bytes to transfer in each block.
+ * blockcount: number of blocks to transfer per timer tick.
+ * period: period of timer in microseconds.  Every timer tick will start a
+ *         new transfer of data blocks
+ * type: type of test; may be 'ram', 'fifo' or 'bcom'.
+ * chipselect: chipselect to use for transfer
+ *
+ * The first test type will copies contents of an LPB address range
+ * using a memcpy.
+ * Usage:
+ * $ echo ram > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * The second test copies contents of an LPB range to RAM using the
+ * LocalPlus FIFO.  The FIFO ISR copies each packet from the FIFO to RAM.
+ * Usage:
+ * $ echo fifo > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * The third test copies contents of an LPB range to RAM using both the FIFO
+ * and the Bestcomm DMA engine.
+ *
+ * Usage:
+ * $ echo bcom > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * All sysfs entries can be read by using cat <parameter>
+ * e.g. cat /sys/devices/platform/lpbtest.0/type will show the test type
+ *
+ * The following is a useful command to dump out all the state of the module:
+ * $ grep '' *
+ *
+ */
+#define DEBUG
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/mempool.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <asm/page.h>
+#include <asm/time.h>
+
+MODULE_AUTHOR("Steven Cavanagh <scavanagh@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+#define DRVNAME "lpbtest"
+
+#define LPBTEST_FLASH_BASE_ADDR		(0xfff00000)
+#define LPBTEST_FLASH_SIZE		(0x00080000)	/* 512 KB */
+#define LPBTEST_FIFO_SIZE		(0x200)		/* FIFO size 512bytes */
+
+#define LPBTEST_STATUS_ABORT	(0x10000000)	/* status register abort  */
+#define LPBTEST_STATUS_NORMAL	(0x01000000)	/* status register normal */
+
+#define LPBTEST_FIFO_OFFSET		0x3C00
+#define LPBTEST_PACKET_SIZE_REG		0x00	/* packet Size register */
+#define LPBTEST_START_ADDR_REG		0x04	/* start Address register */
+#define LPBTEST_CONTROL_REG		0x08	/* control register */
+#define LPBTEST_ENABLE_REG		0x0C	/* enable register */
+#define LPBTEST_STATUS_REG		0x14	/* bytes done status register */
+#define LPBTEST_FIFO_STATUS_REG		0x44	/* FIFO status register */
+#define LPBTEST_FIFO_CNTRL_REG		0x48	/* FIFO control register */
+#define LPBTEST_FIFO_DATA_REG		0x40	/* Data Word register */
+#define LPBTEST_FIFO_ALARM_REG		0x4C	/* FIFO alarm register */
+
+#define LPBTEST_BLOCK_SIZE_MIN		4
+#define LPBTEST_BLOCK_SIZE_MAX		LPBTEST_FIFO_SIZE
+
+/**
+ * lpbtest - Private driver data
+ * @lpb_regs_base: pointer to the LPB's registers
+ * @irq: IRQ of this LPB FIFO
+ * @dev: struct device pointer
+ */
+struct lpbtest {
+	unsigned int irq;
+	void *target_base;
+	void *ram_base;
+	dma_addr_t ram_phys;
+	void __iomem *regs;
+	struct device *dev;
+
+	phys_addr_t fifo_data_base;
+
+	/* Timeslice timer */
+	struct timer_list timer;
+	unsigned long time;		/* next deadline; in jiffies */
+
+	/* Statistics */
+	unsigned long irq_time;
+	unsigned long timer_time;
+	unsigned long copy_time;
+	unsigned long bcom_time;
+	unsigned long start_time;
+	unsigned long stop_time;
+	int data_read;
+	int overrun_count;
+
+	/* state variables */
+	int next_block;	/* Number of next block to send.  If this is
+			 * >= .blockcount, then all the transfers are
+			 * finished */
+
+	struct bcom_task *bcom_task;
+
+	/* sysfs attributes */
+	int action;
+	int type;
+	unsigned blockcount;
+	unsigned blocksize;
+	unsigned period;
+	unsigned chipselect;
+	unsigned offset;
+	phys_addr_t target_phys;
+	int verify;
+
+	spinlock_t lock;
+};
+
+static struct of_device_id immr_ids[] = {
+	{ .compatible = "fsl,mpc5200-immr", },
+	{ .compatible = "fsl,mpc5200b-immr", },
+	{ .type = "soc", .compatible = "mpc5200", }, /* lite5200 */
+	{ .type = "builtin", .compatible = "mpc5200", }, /* efika */
+	{}
+};
+
+/* Helper functions to test selected behaviour */
+static inline int fifotest(struct lpbtest *priv) { return priv->type == 1; }
+
+static void lpbtest_do_next_transfer(struct lpbtest *priv)
+{
+	if (priv->next_block < priv->blockcount) {
+		priv->next_block++;
+		/* Kick off the transaction, Set the restart bit */
+		out_8(priv->regs + LPBTEST_PACKET_SIZE_REG, 0x01);
+	}
+}
+
+/*
+ *
+ * Called from DMA IRQ handler
+ *
+ */
+static void lpbtest_enqueue_next_buffer(struct lpbtest *priv)
+{
+	struct bcom_bd *bd;
+
+	/* Prepare and enqueue the next buffer descriptor */
+	bd = bcom_prepare_next_buffer(priv->bcom_task);
+
+	/* Bytes to be transfered by BestComm */
+	bd->status = LPBTEST_FIFO_SIZE;
+	bd->data[0] = priv->ram_phys; /* Set up destination address */
+
+	/* Give BD to BestComm */
+	bcom_submit_next_buffer(priv->bcom_task, NULL);
+}
+
+static void lpbtest_verify_dma_transfer(struct lpbtest *priv)
+{
+	int ret;
+
+	dev_dbg(priv->dev, "verifying transfer\n");
+	ret = memcmp(priv->ram_base, priv->target_base, priv->blocksize);
+	if (ret)
+		dev_err(priv->dev, "error: corrupt transfer\n");
+}
+
+/* Bestcomm SCLPC DMA irq handler */
+static irqreturn_t lpbtest_bcom_irq(int irq, void *_priv)
+{
+	struct lpbtest *priv = _priv;
+	unsigned long bcom_time = get_tbl();
+
+	/* For each finished block, dequeue the completed block buffer
+	 * and enqueue a new one in it's place. */
+	while (bcom_buffer_done(priv->bcom_task)) {
+		bcom_retrieve_buffer(priv->bcom_task, NULL, NULL);
+		if (priv->verify)
+			lpbtest_verify_dma_transfer(priv);
+
+		lpbtest_enqueue_next_buffer(priv);
+		bcom_enable(priv->bcom_task);
+
+		priv->data_read += priv->blocksize;
+	}
+
+	priv->bcom_time += get_tbl() - bcom_time;
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * SCLPC FIFO peripheral interrupt
+ * Process a FIFO packet size interrupt, then reset the FIFO.
+ */
+static irqreturn_t lpbtest_fifo_irq(int irq, void *_priv)
+{
+	struct lpbtest *priv = _priv;
+	u32 bytes_done_status = 0;
+	u32 fifo_status = 0;
+	u32 *fifo_data_word;
+	unsigned long copy_time;
+	unsigned long irq_time = get_tbl();
+	int i;
+
+	bytes_done_status = in_be32(priv->regs + LPBTEST_STATUS_REG);
+	fifo_status = in_be32(priv->regs + LPBTEST_FIFO_STATUS_REG);
+
+	/* Check the bytes done status register bits */
+	if (bytes_done_status & LPBTEST_STATUS_ABORT) {
+		dev_err(priv->dev, "ABORT TERMINATION ERROR\n");
+		dev_err(priv->dev, "SCLPC STATUS REG:0x%x\n",
+			bytes_done_status);
+
+		/* Clear AT bit */
+		out_8(priv->regs + LPBTEST_STATUS_REG, 0x1);
+		priv->action = 0;
+	}
+
+	if (bytes_done_status & LPBTEST_STATUS_NORMAL) {
+		/* Clear NT bit */
+		out_8(priv->regs + LPBTEST_STATUS_REG, 0x01);
+
+		/* Check the FIFO status register error bits */
+		if (fifo_status & 0x40) {
+			dev_err(priv->dev, "FIFO ERROR\n");
+			dev_err(priv->dev, "FIFO STATUS REG:0x%x\n",
+				fifo_status);
+
+			/* Clear bit */
+			out_be32(priv->regs + LPBTEST_FIFO_STATUS_REG,
+				 (fifo_status & ~0x40));
+			priv->action = 0;
+		}
+
+		/* Kick off next transfer so it can progress while the FIFO
+		 * is being read */
+		lpbtest_do_next_transfer(priv);
+
+		/* Read FIFO, if the FIFO is full,
+		   read it if not running BestComm */
+		if (fifotest(priv)) {
+			/* Copy FIFO bytes */
+			copy_time = get_tbl();
+			fifo_data_word = priv->ram_base;
+			for (i = 0; i < priv->blocksize; i += 4) {
+				*fifo_data_word = in_be32(priv->regs + LPBTEST_FIFO_DATA_REG);
+				fifo_data_word++;
+			}
+			priv->copy_time += get_tbl() - copy_time;
+			priv->data_read += priv->blocksize;
+		}
+	}
+
+	priv->irq_time += get_tbl() - irq_time;
+	return IRQ_HANDLED;
+}
+
+static int lpbtest_register_status_irq(struct device *dev)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	u32 intspec[3] = {2, 23, 0}; /* MPC5200 irq id for the FIFO device */
+	int virq, ret = 0;
+
+	/* Setup the interrupt handlers */
+	virq = irq_create_of_mapping(NULL, intspec, 3);
+	dev_dbg(dev, "virq=%i\n", virq);
+
+	if (virq < 0) {
+		dev_err(dev, "irq_create_of_mapping() error: %d\n", ret);
+		return virq;
+	}
+
+	ret = request_irq(virq, lpbtest_fifo_irq, IRQF_SHARED,
+			  "lpbtest-fifo", priv);
+	if (ret) {
+		dev_err(dev, "request_irq() LPB status error: %d\n", ret);
+		return ret;
+	}
+
+	priv->irq = virq;
+	return ret;
+}
+
+static void lpbtest_stop_test(struct lpbtest *priv)
+{
+	priv->stop_time = get_tbl();
+
+	bcom_disable(priv->bcom_task);
+	while (!bcom_queue_empty(priv->bcom_task))
+		bcom_retrieve_buffer(priv->bcom_task, NULL, NULL);
+}
+
+int lpbtest_check_test_stop(struct device *dev)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+
+	if (!priv->action) {
+		lpbtest_stop_test(priv);
+		return 1;
+	}
+
+	/* Update the timeslice time */
+	priv->time += usecs_to_jiffies(priv->period);
+	if ((int)(priv->time - jiffies) < 0) {
+		dev_info(dev, "Timeslice overrun by %ius; aborting\n",
+			 jiffies_to_usecs(jiffies - priv->time) + priv->period);
+		lpbtest_stop_test(priv);
+		return 1;
+	}
+
+	/* Reset the timer */
+	mod_timer(&priv->timer, priv->time);
+
+	return 0;
+}
+
+static void lpbtest_read_channels_to_ram(unsigned long _dev)
+{
+	struct device *dev = (struct device *)_dev;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	unsigned long time = get_tbl();
+	int i;
+
+	if (lpbtest_check_test_stop(dev))
+		return;
+
+	/* Assume, that all channels have data available */
+	for (i = 0; i < priv->blockcount; i++) {
+		memcpy(priv->ram_base, priv->target_base, priv->blocksize);
+		priv->data_read += priv->blocksize;
+	}
+
+	priv->timer_time += get_tbl() - time;
+}
+
+static void lpbtest_read_channels(unsigned long _dev)
+{
+	struct device *dev = (struct device *)_dev;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	unsigned long flags;
+	unsigned long time = get_tbl();
+
+	if (lpbtest_check_test_stop(dev))
+		return;
+
+	/* Allow the timer to process the present state w/o an interrupt */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (priv->next_block < priv->blockcount) {
+		dev_err(priv->dev, "overrun! next=%i total=%i\n",
+			priv->next_block, priv->blockcount);
+		priv->overrun_count++;
+		goto out;
+	}
+
+	/* This line is the FIFO throttle, the faster the next packet
+	 * is cleared, the faster the FIFO can be read and filled by
+	 * the IRQ.  The ISR will stop handling the FIFO, when all the
+	 * channels have been read.
+	 */
+	priv->next_block = 0;
+	lpbtest_do_next_transfer(priv);
+
+ out:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->timer_time += get_tbl() - time;
+}
+
+static void lpbtest_fifo_start(struct lpbtest *priv)
+{
+	out_be32(priv->regs + LPBTEST_PACKET_SIZE_REG, priv->blocksize);
+
+	/* FIFO receive, BPT 8 bytes/transfer, CS# */
+	out_be32(priv->regs + LPBTEST_CONTROL_REG,
+		 0x00010008 | priv->chipselect << 24);
+
+	/* Kick off the transaction: Set the restart bit for the FIFO */
+	out_8(priv->regs + LPBTEST_PACKET_SIZE_REG, 0x01);
+}
+
+static void lpbtest_bcom_start(struct lpbtest *priv)
+{
+	/* Setup the BestComm engine */
+	bcom_gen_bd_rx_reset(priv->bcom_task);
+	while (!bcom_queue_full(priv->bcom_task))
+		lpbtest_enqueue_next_buffer(priv);
+
+	/* Set the FIFO packet size */
+	out_be32(priv->regs + LPBTEST_PACKET_SIZE_REG, priv->blocksize);
+
+	/* FIFO receive, BPT 8 bytes/transfer, CS# */
+	out_be32(priv->regs + LPBTEST_CONTROL_REG,
+		 0x00010008 | priv->chipselect << 24);
+
+	/* Kick off the transaction: Set the restart bit for the FIFO */
+	out_8(priv->regs + LPBTEST_PACKET_SIZE_REG, 0x01);
+
+	/* Enable the bcom task */
+	bcom_enable(priv->bcom_task);
+}
+
+static const struct lpbtest_type {
+	char *name;
+	void (*start)(struct lpbtest *);
+	void (*timer)(unsigned long);
+} lpbtest_type[] = {
+	{
+		.name = "ram",
+		.timer = lpbtest_read_channels_to_ram,
+	},
+	{
+		.name = "fifo",
+		.start = lpbtest_fifo_start,
+		.timer = lpbtest_read_channels,
+	},
+	{
+		.name = "bcom",
+		.start = lpbtest_bcom_start,
+		.timer = lpbtest_read_channels,
+	}
+};
+
+/* ---------------------------------------------------------------------
+ * sysfs interfaces
+ * --------------------------------------------------------------------- */
+static ssize_t lpbtest_set_type(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	const char *name;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lpbtest_type); i++) {
+		name = lpbtest_type[i].name;
+
+		if (count < strlen(name))
+			continue;
+
+		if (strncmp(buf, name, strlen(name)) == 0) {
+			priv->type = i;
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t lpbtest_show_type(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%s\n", lpbtest_type[priv->type].name);
+}
+
+static ssize_t lpbtest_set_action(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	const struct lpbtest_type *type = &lpbtest_type[priv->type];
+
+	if (strncmp(buf, "start", strlen("start")) == 0)
+		priv->action = 1;
+	else if (strncmp(buf, "stop", strlen("stop")) == 0)
+		priv->action = 0;
+	else {
+		dev_err(dev, "Usage: echo [start,stop] > action\n");
+		return -EINVAL;
+	}
+
+	if (priv->action) {
+		init_timer(&priv->timer);
+		priv->timer.data = (unsigned long) dev;
+
+		priv->irq_time = 0;
+		priv->timer_time = 0;
+		priv->bcom_time = 0;
+		priv->stop_time = priv->start_time = get_tbl();
+		priv->data_read = 0;
+		priv->overrun_count = 0;
+		priv->timer.function = type->timer;
+
+		/* Map the device */
+		priv->target_base = ioremap(priv->target_phys,
+					    LPBTEST_BLOCK_SIZE_MAX);
+		if (!priv->target_base) {
+			dev_err(dev, "Error mapping device\n");
+			return -ENOMEM;
+		}
+
+		dev_dbg(dev, "Started %s test\n", type->name);
+
+		/* Run any setup code */
+		if (type->start)
+			type->start(priv);
+
+		/* Set the expiration time for the timer. */
+		priv->time = jiffies + usecs_to_jiffies(priv->period);
+		mod_timer(&priv->timer, priv->time);
+	}
+
+	return count;
+}
+
+static ssize_t lpbtest_show_action(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	char *action;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+
+	action = (priv->action == 1) ? "start" : "stop";
+	return sprintf(buf, "%s\n", action);
+}
+
+/*
+ * Export a blockcount attr
+ */
+static ssize_t lpbtest_set_blockcount(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	unsigned long temp;
+
+	if (strict_strtoul(buf, 10, &temp))
+		return -EINVAL;
+	priv->blockcount = temp;
+
+	return count;
+}
+
+static ssize_t lpbtest_show_blockcount(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", priv->blockcount);
+}
+
+/*
+ * Export a blocksize attr
+ */
+static ssize_t lpbtest_set_blocksize(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	unsigned long temp;
+
+	if (strict_strtoul(buf, 10, &temp))
+		return -EINVAL;
+
+	if ((temp < LPBTEST_BLOCK_SIZE_MIN) || (temp > LPBTEST_BLOCK_SIZE_MAX))
+		return -EINVAL;
+
+	priv->blocksize = temp & 0xfffffffc;
+	return count;
+}
+
+static ssize_t lpbtest_show_blocksize(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", priv->blocksize);
+}
+
+static ssize_t lpbtest_set_period(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	long temp;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+
+	if (strict_strtol(buf, 0, &temp)) {
+		dev_err(dev, "Usage: echo [period (us)] > period\n");
+		return -EINVAL;
+	}
+	priv->period = temp;
+	return count;
+}
+
+static ssize_t lpbtest_show_period(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", priv->period);
+}
+
+static ssize_t lpbtest_set_cs(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	long temp;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+
+	if (strict_strtol(buf, 0, &temp))
+		return -EINVAL;
+
+	if (temp > 7)
+		return -EINVAL;
+
+	priv->chipselect = temp;
+	return count;
+}
+
+static ssize_t lpbtest_show_cs(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", priv->chipselect);
+}
+
+static ssize_t lpbtest_set_baseaddr(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	unsigned long temp;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+
+	if (strict_strtoul(buf, 0, &temp))
+		return -EINVAL;
+
+	priv->target_phys = temp;
+	return count;
+}
+
+static ssize_t lpbtest_show_baseaddr(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%llx\n", (unsigned long long) priv->target_phys);
+}
+
+static ssize_t lpbtest_set_verify(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	unsigned long temp;
+
+	if (strict_strtoul(buf, 10, &temp))
+		return -EINVAL;
+
+	priv->verify = temp;
+	return count;
+}
+
+static ssize_t lpbtest_show_verify(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", priv->verify);
+}
+
+static ssize_t lpbtest_show_results(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	int systime, realtime, utilization, rate, c;
+
+	realtime = priv->stop_time - priv->start_time;
+	systime = priv->timer_time + priv->irq_time + priv->bcom_time;
+	utilization = systime / (realtime / 10000);
+	rate = priv->data_read / (realtime / (tb_ticks_per_usec * 100));
+
+	c = sprintf(buf,      "real:		%10uticks	%9luus\n",
+		    realtime, realtime / tb_ticks_per_usec);
+	c += sprintf(buf + c, "sys:		%10uticks	%9luus\n",
+		     systime, systime / tb_ticks_per_usec);
+	c += sprintf(buf + c, "timer:		%10luticks	%9luus\n",
+		     priv->timer_time, priv->timer_time / tb_ticks_per_usec);
+	c += sprintf(buf + c, "fifo-irq:	%10luticks	%9luus\n",
+		     priv->irq_time, priv->irq_time / tb_ticks_per_usec);
+	c += sprintf(buf + c, "bcom-irq:	%10luticks	%9luus\n",
+		     priv->bcom_time, priv->bcom_time / tb_ticks_per_usec);
+	c += sprintf(buf + c, "overruns:	%10u\n", priv->overrun_count);
+	c += sprintf(buf + c, "%%CPU:		%10u.%.2u%%\n",
+		     utilization / 100, utilization % 100);
+	c += sprintf(buf + c, "byte count:	%10u\n", priv->data_read);
+	c += sprintf(buf + c, "data rate:	%10u.%.2uMB/s\n",
+		     rate / 100, rate % 100);
+
+	return c;
+}
+
+static struct device_attribute lpbtest_attrib[] = {
+	__ATTR(action, S_IWUSR | S_IRUGO,
+	       lpbtest_show_action, lpbtest_set_action),
+	__ATTR(blockcount, S_IWUSR | S_IRUGO,
+	       lpbtest_show_blockcount, lpbtest_set_blockcount),
+	__ATTR(blocksize, S_IWUSR | S_IRUGO,
+	       lpbtest_show_blocksize, lpbtest_set_blocksize),
+	__ATTR(period, S_IWUSR | S_IRUGO,
+	       lpbtest_show_period, lpbtest_set_period),
+	__ATTR(chipselect, S_IWUSR | S_IRUGO,
+	       lpbtest_show_cs, lpbtest_set_cs),
+	__ATTR(baseaddr, S_IWUSR | S_IRUGO,
+	       lpbtest_show_baseaddr, lpbtest_set_baseaddr),
+	__ATTR(verify, S_IWUSR | S_IRUGO,
+	       lpbtest_show_verify, lpbtest_set_verify),
+	__ATTR(type, S_IWUSR | S_IRUGO,
+	       lpbtest_show_type, lpbtest_set_type),
+	__ATTR(results, S_IWUSR | S_IRUGO, lpbtest_show_results, NULL),
+};
+
+static void lpbtest_cleanup_sysfs(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lpbtest_attrib); i++)
+		device_remove_file(dev, &lpbtest_attrib[i]);
+}
+
+static int lpbtest_setup(struct device *dev)
+{
+	struct device_node *np;
+	struct resource res;
+	struct lpbtest *priv = dev_get_drvdata(dev);
+	int ret = 0;
+	phys_addr_t phys_addr;
+
+	/* Allocate a destination buffer */
+	priv->ram_base = kzalloc(LPBTEST_BLOCK_SIZE_MAX, GFP_KERNEL);
+	if (!priv->ram_base) {
+		dev_err(dev, "Error allocating test buffer\n");
+		return -ENOMEM;
+	}
+	priv->ram_phys = virt_to_phys(priv->ram_base);
+
+	/* map the whole register space */
+	np = of_find_matching_node(NULL, immr_ids);
+	if (!np) {
+		dev_err(dev, "bcomm_test_init():Unable to locate platform\n");
+		return -ENODEV;
+	}
+	if (of_address_to_resource(np, 0, &res)) {
+		dev_err(dev, "Unable to locate resources\n");
+		return -ENODEV;
+	}
+
+	phys_addr = res.start + LPBTEST_FIFO_OFFSET;
+	priv->fifo_data_base = phys_addr + LPBTEST_FIFO_DATA_REG;
+	dev_info(dev, "FIFO regs at address %llx\n",
+		 (unsigned long long)phys_addr);
+	priv->regs = ioremap(phys_addr, 0x50);
+	if (!priv->regs) {
+		dev_err(dev, "bcomm_test_init():Unable to locate platform\n");
+		return -ENODEV;
+	}
+	of_node_put(np);
+
+	/* Reset LPB FIFO */
+	out_be32(priv->regs + LPBTEST_ENABLE_REG, 0x01010000);
+
+	/* Write the start address; Offset from chipselect base address */
+	out_be32(priv->regs + LPBTEST_START_ADDR_REG, 0);
+
+	/* Write the control register */
+	/* CS0 asserted, FIFO receive, and BPT 8 bytes/transfer */
+	out_be32(priv->regs + LPBTEST_CONTROL_REG, 0x00010008);
+
+	/* Set AIE, NIE, and ME bits */
+	out_be32(priv->regs + LPBTEST_ENABLE_REG, 0x00000301);
+
+	/* Set alarm when only 256 bytes remain in FIFO */
+	out_be32(priv->regs + LPBTEST_FIFO_ALARM_REG, 0x100);
+
+	/* Stop requesting data when 4 bytes remaining in FIFO */
+	out_be32(priv->regs + LPBTEST_FIFO_CNTRL_REG, 0x01000000);
+
+	/* Register the SCLPC status register ISR(), and
+	 * set the packet size to the FIFO size. We want to
+	 * interrupt when the FIFO is full to empty it without
+	 * a BestComm task */
+	ret = lpbtest_register_status_irq(dev);
+	if (ret)
+		return ret;
+
+	/* Setup the bcom gen bd task */
+	priv->bcom_task = bcom_gen_bd_rx_init(32, priv->fifo_data_base,
+					      BCOM_INITIATOR_SCLPC,
+					      BCOM_IPR_SCLPC, 512);
+	if (!priv->bcom_task) {
+		dev_err(dev, "error initializing bestcomm task\n");
+		return -ENODEV;
+	}
+
+	/* Setup the bcom_task irq */
+	ret = request_irq(bcom_get_task_irq(priv->bcom_task), &lpbtest_bcom_irq,
+			  IRQF_SHARED, "lpbtest-bcom", priv);
+	if (ret)
+		dev_err(dev, "error registering Bestcomm task irq: %d\n", ret);
+
+	return ret;
+}
+
+static int __devinit lpbtest_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct lpbtest *priv;
+
+	int ret = 0, i;
+
+	/* Allocate and initialize the driver private data */
+	priv = kzalloc(sizeof *priv, GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+	priv->blockcount = 16;
+	priv->blocksize = LPBTEST_FIFO_SIZE;
+	priv->period  = 20000;
+	priv->target_phys = LPBTEST_FLASH_BASE_ADDR;
+
+	spin_lock_init(&priv->lock);
+	platform_set_drvdata(pdev, priv);
+
+	ret = lpbtest_setup(dev);
+	if (ret) {
+		dev_err(dev, "lpbtest_setup() error\n");
+		return ret;
+	}
+
+	/* Register the SYSFS files */
+	for (i = 0; i < ARRAY_SIZE(lpbtest_attrib); i++) {
+		ret = device_create_file(dev, &lpbtest_attrib[i]);
+		if (ret) {
+			dev_err(dev, "error creating sysfs files (%d)\n", ret);
+			lpbtest_cleanup_sysfs(pdev);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int __devexit lpbtest_remove(struct platform_device *pdev)
+{
+	struct lpbtest *priv = platform_get_drvdata(pdev);
+
+	if (priv->irq)
+		free_irq(priv->irq, priv);
+
+	if (priv->bcom_task) {
+		free_irq(bcom_get_task_irq(priv->bcom_task), priv);
+		bcom_gen_bd_rx_release(priv->bcom_task);
+	}
+
+	lpbtest_cleanup_sysfs(pdev);
+
+	del_timer(&priv->timer);
+
+	kfree(priv->ram_base);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver lpbtest_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = DRVNAME,
+	},
+	.probe  = lpbtest_probe,
+	.remove = __devexit_p(lpbtest_remove),
+};
+
+static struct platform_device *lpbtest_pdev;
+static int __init lpbtest_init(void)
+{
+	int rc;
+
+	lpbtest_pdev = platform_device_register_simple(DRVNAME, 0, NULL, 0);
+	if (!lpbtest_pdev) {
+		pr_err("%s: error registering test device\n", DRVNAME);
+		return -ENOMEM;
+	}
+
+	rc = platform_driver_register(&lpbtest_driver);
+	if (rc)
+		platform_device_unregister(lpbtest_pdev);
+	return rc;
+}
+module_init(lpbtest_init);
+
+static void lpbtest_exit(void)
+{
+	platform_device_unregister(lpbtest_pdev);
+	platform_driver_unregister(&lpbtest_driver);
+}
+module_exit(lpbtest_exit);
-- 
1.5.6.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-20 16:04 ` Grant Likely
@ 2009-01-21 12:57   ` Dave Best
  2009-01-21 15:16     ` Jon Smirl
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Best @ 2009-01-21 12:57 UTC (permalink / raw)
  To: Grant Likely; +Cc: linuxppc-dev

Thanks for the driver.=20
I am looking into this.

I still have some trouble to compile and use the driver:
- I currently use the 2.6.23.1 kernel source which was provide along with  =
the pcm030 board by Phytec. And i guess your driver was compile along with =
a newer driver as i can't find the "strict_strtoul" and "strict_strtol" fun=
ctions in my kernel but i copied their source into my code. The same with "=
of_find_matching_node" which i tried to exchange with "of_find_compatible_n=
ode" but somehow my devicetree does not include the neccesay compatible typ=
es. Here I tried "of_find_node_by_type" with "soc" and it seemed to work.
The biggest problem seems to be "irq_create_of_mapping" for the localplus i=
sr. It's giving me a kernel OOPS by trying to access data from 0x00000010.

If you could point out if I have to use a newer Kernel, and which, to solve=
 these problems or how i can code around these with my current kernel sourc=
e.


Anyway, I looked over your code and only found that you first create an int=
errupt routine for the localplus in general before creating the bestcomm ta=
sk.
As far as I thought it was enough to create the bestcomm task and enable it=
. And it was upon Bestcomm to watch over its tasks and to look for interrup=
t sources according to their initiators/requestors to trigger their ISR.

In your example the local plus fifo would trigger its ISR when its done tra=
nsfering the blocksize and reset the status bits and start over. The task w=
ould run its ISR when its buffer is filled to create a new one.

If thats the way to go, i will try it out.

Thanks
Dave

--- Grant Likely <grant.likely@secretlab.ca> schrieb am Di, 20.1.2009:

> Von: Grant Likely <grant.likely@secretlab.ca>
> Betreff: Re: Bestcomm tasks and interrupts on MPC5200(B)
> An: arieswar24b@yahoo.de
> CC: linuxppc-dev@ozlabs.org
> Datum: Dienstag, 20. Januar 2009, 17:04
> On Tue, Jan 20, 2009 at 6:15 AM, Dave Best
> <arieswar24b@yahoo.de> wrote:
> > I'm trying to write a driver which uses the Local
> Plus Bus on my MPC5200B and therefore have to use BestComm
> DMA, which requires me to use a Gen_BD task for data
> transfer with Local Plus.
> > I tried to follow the fec driver that is currently
> used and took a peek at the mpc52xx-ac97 driver which at
> least uses the same kind of bus as I.
> >
> > Initialising the task, resetting and enabling works
> fine. Even request_irq reports no error, but when I start a
> transfer it hangs and if I am lucky, an interrupt occurs
> after quite some time. But it's always the BestComm
> ethernet rx task which produces an RFIFO interrupt,
> presumably after the watchdog catches on.
> > If this happens my interrupt occurs to.
>=20
> Are you using the LocalPlus fifo device for the transfer
> (you need to
> if you aren't)?
>=20
> I've attached a test driver that demonstrates how to do
> FIFO only and
> FIFO+DMA transfers over the localplus bus.
>=20
> g.
>=20
>=20
>=20
>=20
> --=20
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.=0A=0A=0A      

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-21 12:57   ` Dave Best
@ 2009-01-21 15:16     ` Jon Smirl
  2009-01-21 15:26       ` Wolfram Sang
  0 siblings, 1 reply; 7+ messages in thread
From: Jon Smirl @ 2009-01-21 15:16 UTC (permalink / raw)
  To: arieswar24b, Sascha Hauer; +Cc: linuxppc-dev

On Wed, Jan 21, 2009 at 7:57 AM, Dave Best <arieswar24b@yahoo.de> wrote:
> Thanks for the driver.
> I am looking into this.
>
> I still have some trouble to compile and use the driver:
> - I currently use the 2.6.23.1 kernel source which was provide along with=
  the pcm030 board by Phytec. And i guess your driver was compile along wit=
h a newer driver as i can't find the "strict_strtoul" and "strict_strtol" f=
unctions in my kernel but i copied their source into my code. The same with=
 "of_find_matching_node" which i tried to exchange with "of_find_compatible=
_node" but somehow my devicetree does not include the neccesay compatible t=
ypes. Here I tried "of_find_node_by_type" with "soc" and it seemed to work.
> The biggest problem seems to be "irq_create_of_mapping" for the localplus=
 isr. It's giving me a kernel OOPS by trying to access data from 0x00000010=
.
>
> If you could point out if I have to use a newer Kernel, and which, to sol=
ve these problems or how i can code around these with my current kernel sou=
rce.

Current git kernel runs fine on the phytec board without patches.

Pengutronix makes the kernels for the phytec pcm030 boards. Their
2.6.23 kernel has probably been patched for real time support. If you
don't need real time there's no real downside to using current git.
Sascha may have some other patches that I'm unaware of.

>
>
> Anyway, I looked over your code and only found that you first create an i=
nterrupt routine for the localplus in general before creating the bestcomm =
task.
> As far as I thought it was enough to create the bestcomm task and enable =
it. And it was upon Bestcomm to watch over its tasks and to look for interr=
upt sources according to their initiators/requestors to trigger their ISR.
>
> In your example the local plus fifo would trigger its ISR when its done t=
ransfering the blocksize and reset the status bits and start over. The task=
 would run its ISR when its buffer is filled to create a new one.
>
> If thats the way to go, i will try it out.
>
> Thanks
> Dave
>
> --- Grant Likely <grant.likely@secretlab.ca> schrieb am Di, 20.1.2009:
>
>> Von: Grant Likely <grant.likely@secretlab.ca>
>> Betreff: Re: Bestcomm tasks and interrupts on MPC5200(B)
>> An: arieswar24b@yahoo.de
>> CC: linuxppc-dev@ozlabs.org
>> Datum: Dienstag, 20. Januar 2009, 17:04
>> On Tue, Jan 20, 2009 at 6:15 AM, Dave Best
>> <arieswar24b@yahoo.de> wrote:
>> > I'm trying to write a driver which uses the Local
>> Plus Bus on my MPC5200B and therefore have to use BestComm
>> DMA, which requires me to use a Gen_BD task for data
>> transfer with Local Plus.
>> > I tried to follow the fec driver that is currently
>> used and took a peek at the mpc52xx-ac97 driver which at
>> least uses the same kind of bus as I.
>> >
>> > Initialising the task, resetting and enabling works
>> fine. Even request_irq reports no error, but when I start a
>> transfer it hangs and if I am lucky, an interrupt occurs
>> after quite some time. But it's always the BestComm
>> ethernet rx task which produces an RFIFO interrupt,
>> presumably after the watchdog catches on.
>> > If this happens my interrupt occurs to.
>>
>> Are you using the LocalPlus fifo device for the transfer
>> (you need to
>> if you aren't)?
>>
>> I've attached a test driver that demonstrates how to do
>> FIFO only and
>> FIFO+DMA transfers over the localplus bus.
>>
>> g.
>>
>>
>>
>>
>> --
>> Grant Likely, B.Sc., P.Eng.
>> Secret Lab Technologies Ltd.
>
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>



--=20
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Bestcomm tasks and interrupts on MPC5200(B)
  2009-01-21 15:16     ` Jon Smirl
@ 2009-01-21 15:26       ` Wolfram Sang
  0 siblings, 0 replies; 7+ messages in thread
From: Wolfram Sang @ 2009-01-21 15:26 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linuxppc-dev, arieswar24b

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

Hello,

> Pengutronix makes the kernels for the phytec pcm030 boards. Their
> 2.6.23 kernel has probably been patched for real time support. If you
> don't need real time there's no real downside to using current git.
> Sascha may have some other patches that I'm unaware of.

We are currently working on a new release for both, the pcm030 and
pcm032 having 2.6.28 for non-RT and 2.6.26 for RT.

Kind regards,

   Wolfram

-- 
  Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de
 Pengutronix - Linux Solutions for Science and Industry

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-01-21 15:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-20 13:15 Bestcomm tasks and interrupts on MPC5200(B) Dave Best
2009-01-20 15:51 ` Frank Bennett
2009-01-20 15:59 ` Frank Bennett
2009-01-20 16:04 ` Grant Likely
2009-01-21 12:57   ` Dave Best
2009-01-21 15:16     ` Jon Smirl
2009-01-21 15:26       ` Wolfram Sang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.