The original version was done by Grant Likely. This is a modified
version using the platform LocalPlus driver.

Signed-off-by: Roman Fietze <roman.fie...@telemotive.de>
---
 drivers/misc/mpc5200-localplus-test.c |  879 +++++++++++++++++++++++++++++++++
 1 files changed, 879 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/mpc5200-localplus-test.c

diff --git a/drivers/misc/mpc5200-localplus-test.c 
b/drivers/misc/mpc5200-localplus-test.c
new file mode 100644
index 0000000..dcfb282
--- /dev/null
+++ b/drivers/misc/mpc5200-localplus-test.c
@@ -0,0 +1,879 @@
+/*
+ * 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, bpt, 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 or distance (leading '+') in jiffies
+ *         ("[1-9]{1,}j") or microseconds.  Every timer tick will
+ *         start a new transfer of data blocks. If this value is 0,
+ *         new transfers will be started as quickly as possible using
+ *         a tasklet.
+ * type: type of test; may be 'ram', 'fifo' or 'bcom'.
+ * chipselect: chipselect to use for transfer
+ *
+ * The first test 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/ctype.h>
+#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>
+#include <asm/cacheflush.h>
+#include <asm/div64.h>
+
+MODULE_AUTHOR("Steven Cavanagh <scavan...@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+
+#define DRVNAME "lpbtest"
+
+#define LPBTEST_FLASH_BASE_ADDR                (0xff000000)
+#define LPBTEST_FLASH_SIZE             (0x01000000)    /* 16 MiB */
+
+#define LPBTEST_BLOCK_SIZE_MIN         4
+#define LPBTEST_BLOCK_SIZE_MAX         (0x20000)       /* 128 KiB */
+
+#define LPBTEST_JIFFIES_MIN            0
+
+/* Address of SCLPC relative to MBAR
+ *
+ * TODO: move to OF tree?
+ */
+#define MPC52xx_SCLPC_OFFSET           0x3C00
+
+
+/**
+ * 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 {
+       struct device *dev;
+
+       /* sysfs attributes */
+       int action;
+       size_t type;
+       int write;              /* true for write test */
+       size_t blockcount;
+       size_t blocksize;
+       unsigned long period;   /* in jiffies */
+       int period_us;          /* true: period is in us, false: in jiffies */
+       int period_delta;       /* true: period is time between the
+                                * transactions, false: tme between
+                                * start of two transactions */
+       unsigned int chipselect;
+       int verify;
+
+       spinlock_t lock;
+
+       void *ram_virt;
+
+       void *dev_virt;
+       phys_addr_t dev_phys;
+
+       struct mpc52xx_lpbfifo_request req;
+
+       /* Timeslice timer */
+       struct timer_list timer;
+       unsigned long tnext;            /* next deadline; in jiffies */
+
+       /* Statistics */
+       unsigned long irq_time;
+       unsigned long timer_time;
+       unsigned long bcom_time;
+       unsigned long start_time;
+       unsigned long stop_time;
+       unsigned long long data_read;
+       int overrun_count;
+
+       /* state variables */
+       size_t next_block;      /* Number of next block to send. If
+                                * this is >= blockcount, then all the
+                                * transfers are finished */
+
+       size_t rcvd_blocks;     /* Number of blocks received. */
+};
+
+/* Helper functions to test selected behaviour */
+static inline int lpbtest_isfifo(struct lpbtest *priv)
+{
+       return priv->type == 1;
+}
+
+
+static void lpbtest_stop(struct lpbtest *priv)
+{
+       priv->stop_time = get_tbl();
+       dev_dbg(priv->dev, "%s() stop_time=%lu\n", __FUNCTION__, 
priv->stop_time);
+}
+
+
+static inline unsigned long lpbtest_period_in_jiffies(struct lpbtest *priv)
+{
+       return priv->period_us ? usecs_to_jiffies(priv->period) : priv->period;
+}
+
+
+/* Start timer. In case finished is false this routine is called at
+ * the start of a transfer, if finished is true at the end of a
+ * transfer.
+ *
+ * In the first case we only start the timer if a period >
+ * LPBTEST_PERIOD_ISO_LIMIT is set, in the second case we only start
+ * the timer if a period <= LPBTEST_PERIOD_ISO_LIMIT is set.
+ */
+static void lpbtest_start_timer(struct lpbtest *priv, int finished)
+{
+       int ret;
+       if (finished) {
+               if (priv->period_delta) {
+                       priv->tnext = lpbtest_period_in_jiffies(priv);
+#if LPBTEST_JIFFIES_MIN
+                       if (priv->tnext < LPBTEST_JIFFIES_MIN)
+                               priv->tnext = LPBTEST_JIFFIES_MIN;
+#endif
+                       priv->tnext += jiffies;
+                       ret = mod_timer(&priv->timer, priv->tnext);
+                       dev_dbg(priv->dev, "%s(%d): j=%lu tnext=%lu r=%d\n", 
__FUNCTION__, finished, jiffies, priv->tnext, ret);
+                       return;
+               }
+       }
+       else {
+               if (!priv->period_delta) {
+                       priv->tnext += lpbtest_period_in_jiffies(priv);
+                       /* dev_dbg(dev, "%s: jiffies=%lu time=%lu\n", 
__FUNCTION__, jiffies, priv->tnext); */
+                       if (time_is_before_jiffies(priv->tnext)) {
+                               dev_err(priv->dev, "Timeslice overrun by %i us; 
aborting\n",
+                                       jiffies_to_usecs(jiffies - 
priv->tnext));
+                               lpbtest_stop(priv);
+                               return;
+                       }
+
+                       ret = mod_timer(&priv->timer, priv->tnext);
+                       dev_dbg(priv->dev, "%s(%d): j=%lu tnext=%lu r=%d\n", 
__FUNCTION__, finished, jiffies, priv->tnext, ret);
+                       return;
+               }
+       }
+
+       dev_dbg(priv->dev, "%s(%d): j=%lu no action\n", __FUNCTION__, finished, 
jiffies);
+}
+
+static inline int lpbtest_check_stop(struct device *dev)
+{
+       struct lpbtest *priv = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s()\n", __FUNCTION__);
+
+       if (!priv->action) {
+               lpbtest_stop(priv);
+               return 1;
+       }
+
+       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);
+       int i;
+
+       dev_dbg(dev, "%lu: %s()\n", jiffies, __FUNCTION__);
+
+       if (!lpbtest_check_stop(dev)) {
+               const unsigned long tstart = get_tbl();
+
+               lpbtest_start_timer(priv, 0);
+
+               /* Assume, that all channels have data available */
+               for (i = 0; i < priv->blockcount; i++) {
+                       memcpy(priv->ram_virt, priv->dev_virt, priv->blocksize);
+                       priv->data_read += priv->blocksize;
+               }
+
+               priv->timer_time += get_tbl() - tstart;
+
+               lpbtest_start_timer(priv, 1);
+       }
+}
+
+
+static void lpbtest_fifo_done(struct mpc52xx_lpbfifo_request *req)
+{
+       struct lpbtest *priv = req->priv;
+
+       // dev_info(priv->dev, "%s()\n", __FUNCTION__);
+
+       if (priv->verify) {
+               if (memcmp(priv->ram_virt, priv->dev_virt, priv->blocksize)) {
+                       print_hex_dump(KERN_INFO, "RAM: ", DUMP_PREFIX_OFFSET,
+                                      16, sizeof(uint32_t),
+                                      priv->ram_virt, priv->blocksize, 0);
+                       print_hex_dump(KERN_INFO, "DEV: ", DUMP_PREFIX_OFFSET,
+                                      16, sizeof(uint32_t),
+                                      priv->dev_virt, priv->blocksize, 0);
+               }
+               else {
+                       /* dev_info(priv->dev, "memcmp OK\n"); */
+               }
+       }
+
+       lpbtest_start_timer(priv, 1);
+}
+
+
+static void lpbtest_do_next_transfer(struct lpbtest *priv)
+{
+       dev_dbg(priv->dev, "%s()\n", __FUNCTION__);
+
+       if (priv->next_block < priv->blockcount) {
+
+               int err;
+               struct mpc52xx_lpbfifo_request *req = &priv->req;
+
+               memset(req, 0, sizeof(*req));
+               req->cs = priv->chipselect;
+               req->offset = priv->dev_phys - LPBTEST_FLASH_BASE_ADDR;
+               req->data = priv->ram_virt;
+               req->size = priv->blocksize;
+               req->pos = 0;
+               req->flags = MPC52XX_LPBFIFO_FLAG_NO_INCREMENT;
+               req->flags |= priv->write ? MPC52XX_LPBFIFO_FLAG_WRITE : 
MPC52XX_LPBFIFO_FLAG_READ;
+               if (lpbtest_isfifo(priv))
+                       req->flags |= MPC52XX_LPBFIFO_FLAG_NO_DMA;
+               req->callback = lpbtest_fifo_done;
+               req->priv = priv;
+
+               priv->next_block++;
+               dev_dbg(priv->dev, "%s: next_block=%zu\n", __FUNCTION__, 
priv->next_block);
+
+               memset(priv->ram_virt, 0x55, priv->blocksize);
+               err = mpc52xx_lpbfifo_submit(req);
+               if (err) {
+                       dev_err(priv->dev, "cannot submit FIFO request: %d\n", 
err);
+                       lpbtest_stop(priv);
+               }
+       }
+}
+
+static void lpbtest_read_channels(unsigned long _dev)
+{
+       struct device *dev = (struct device *)_dev;
+
+       dev_dbg(dev, "%lu: %s()\n", jiffies, __FUNCTION__);
+
+       if (!lpbtest_check_stop(dev)) {
+               struct lpbtest *priv = dev_get_drvdata(dev);
+               const unsigned long tstart = get_tbl();
+
+               lpbtest_start_timer(priv, 0);
+
+               /* 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 = priv->rcvd_blocks = 0;
+               // dev_dbg(dev, "%s: next_block=%zu\n", __FUNCTION__, 
priv->next_block);
+               lpbtest_do_next_transfer(priv);
+
+               priv->timer_time += get_tbl() - tstart;
+       }
+}
+
+
+static const struct lpbtest_type {
+       char *name;
+       void (*test_timer)(unsigned long);
+} lpbtest_type[] = {
+       {
+               .name = "ram",
+               .test_timer = lpbtest_read_channels_to_ram,
+       },
+       {
+               .name = "fifo",
+               .test_timer = lpbtest_read_channels,
+       },
+       {
+               .name = "bcom",
+               .test_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;
+       size_t 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_dir(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct lpbtest *priv = dev_get_drvdata(dev);
+
+       if (strncmp(buf, "read", strlen("read")) == 0) {
+               priv->write = 0;
+       }
+       else if (strncmp(buf, "write", strlen("write")) == 0) {
+               priv->write = 1;
+       }
+       else {
+               dev_err(dev, "Usage: echo [read,write] > dir\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t lpbtest_show_dir(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lpbtest *priv = dev_get_drvdata(dev);
+       return sprintf(buf, "%s\n", priv->write ? "write" : "read");
+}
+
+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];
+       int oldaction = priv->action;
+
+       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;
+       }
+
+       dev_dbg(dev, "%s: action=%d->%d\n", __FUNCTION__, oldaction, 
priv->action);
+
+       if (oldaction != priv->action) {
+               oldaction = priv->action;
+
+               if (priv->action) {
+                       init_timer(&priv->timer);
+                       priv->timer.function = type->test_timer;
+                       priv->timer.data = (unsigned long)dev;
+
+                       priv->next_block = priv->rcvd_blocks = 0;
+                       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;
+
+                       /* Map the device */
+                       priv->dev_virt = ioremap(priv->dev_phys, 
LPBTEST_BLOCK_SIZE_MAX);
+                       if (!priv->dev_virt) {
+                               dev_err(dev, "Error mapping device\n");
+                               return -ENOMEM;
+                       }
+
+                       printk(KERN_INFO "Started %s test, blocksize=0x%zx\n", 
type->name, priv->blocksize);
+
+                       /* Set the expiration time for the timer. */
+                       priv->tnext = jiffies + 1;
+                       mod_timer(&priv->timer, priv->tnext);
+
+               }
+               else {
+                       del_timer_sync(&priv->timer);
+                       lpbtest_stop(priv);
+
+                       printk(KERN_INFO "Stopped %s test, blocksize=0x%zx\n", 
type->name, priv->blocksize);
+               }
+       }
+
+       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, "%zu\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, 0, &temp))
+               return -EINVAL;
+
+       if (temp < LPBTEST_BLOCK_SIZE_MIN || temp > LPBTEST_BLOCK_SIZE_MAX)
+               return -EINVAL;
+
+       priv->blocksize = temp;
+       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, "%zu\n", priv->blocksize);
+}
+
+static ssize_t lpbtest_set_period(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       unsigned long temp;
+       struct lpbtest *priv = dev_get_drvdata(dev);
+
+       const char *pb = buf;
+       char *pe;
+
+       priv->period_delta = 0;
+       if (*pb == '+') {
+               priv->period_delta = 1;
+               pb++;
+       }
+       temp = simple_strtoul(pb, &pe, 10);
+       if (buf == pe) {
+               dev_err(dev, "period \"%s\" empty?\n", buf);
+               return -EINVAL;
+       }
+
+       if (*pe == 'j') {
+               priv->period_us = 0;
+       }
+       else if (isspace(*pe) || *pe == '\0') {
+               priv->period_us = 1;
+       }
+       else {
+               dev_err(dev, "period \"%s\" invalid\n", buf);
+               return -EINVAL;
+       }
+
+       priv->period = temp;
+       dev_dbg(dev, "period \"%s\" = %s%lu %s\n",
+               buf,
+               priv->period_delta ? "+" : "", temp,
+               priv->period_us ? "us" : "j");
+
+       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, "%s%lu%s\n",
+                      priv->period_delta ? "+" : "",
+                      priv->period,
+                      priv->period_us ? "" : "j");
+}
+
+static ssize_t lpbtest_set_cs(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;
+
+       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, "%u\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->dev_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->dev_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 c;
+       unsigned long systime;
+       unsigned long realtime;
+       unsigned int utilization;
+       unsigned long rate;
+       unsigned long netrate;
+       unsigned long long tmp;
+
+       realtime = priv->stop_time - priv->start_time;
+       systime = priv->timer_time + priv->irq_time + priv->bcom_time;
+
+       if (!realtime) {
+               realtime = get_tbl() - priv->start_time;
+       }
+
+       if (realtime) {
+               tmp = systime * 10000ULL;
+               do_div(tmp, realtime);
+               utilization = tmp;
+
+               tmp = priv->data_read;
+               tmp *= tb_ticks_per_usec;
+               tmp *= 100;
+               do_div(tmp, realtime);
+               rate = tmp;
+       }
+       else {
+               utilization = 0;
+               rate = 0;
+       }
+
+       if (systime) {
+               tmp = priv->data_read;
+               tmp *= tb_ticks_per_usec;
+               tmp *= 100;
+               do_div(tmp, systime);
+               netrate = tmp;
+       }
+       else {
+               netrate = 0;
+       }
+
+       c = sprintf(buf,      "real\t\t:\t%10lu ticks\t%9lu us\n",
+                   realtime, realtime / tb_ticks_per_usec);
+       c += sprintf(buf + c, "sys\t\t:\t%10lu ticks\t%9lu us\n",
+                    systime, systime / tb_ticks_per_usec);
+       c += sprintf(buf + c, "timer\t\t:\t%10lu ticks\t%9lu us\n",
+                    priv->timer_time, priv->timer_time / tb_ticks_per_usec);
+       c += sprintf(buf + c, "fifo irq\t:\t%10lu ticks\t%9lu us\n",
+                    priv->irq_time, priv->irq_time / tb_ticks_per_usec);
+       c += sprintf(buf + c, "bcom irq\t:\t%10lu ticks\t%9lu us\n",
+                    priv->bcom_time, priv->bcom_time / tb_ticks_per_usec);
+       c += sprintf(buf + c, "overruns\t:\t%10u\n", priv->overrun_count);
+       c += sprintf(buf + c, "%%CPU\t\t:\t%10u.%.2u %%\n",
+                    utilization / 100, utilization % 100);
+       c += sprintf(buf + c, "byte count\t:\t%10llu\n", priv->data_read);
+       c += sprintf(buf + c, "net data rate\t:\t%10lu.%.2lu MB/s\n",
+                    rate / 100, rate % 100);
+       c += sprintf(buf + c, "max data rate\t:\t%10lu.%.2lu MB/s\n",
+                    netrate / 100, netrate % 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(dir, S_IWUSR | S_IRUGO,
+              lpbtest_show_dir, lpbtest_set_dir),
+       __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 lpbtest *priv = dev_get_drvdata(dev);
+
+       /* Allocate a destination buffer */
+       priv->ram_virt = kzalloc(LPBTEST_BLOCK_SIZE_MAX, GFP_KERNEL);
+       if (!priv->ram_virt) {
+               dev_err(dev, "Error allocating test buffer\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+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 = dev;
+       priv->blockcount = 16;
+       priv->blocksize = MPC52xx_SCLPC_FIFO_SIZE;
+       priv->period  = 20000;
+       priv->period_us = 1;
+       priv->period_delta = 0;
+       priv->dev_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);
+
+       lpbtest_cleanup_sysfs(pdev);
+
+       del_timer(&priv->timer);
+
+       kfree(priv->ram_virt);
+       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;
+
+       printk(KERN_INFO "%s init\n", DRVNAME);
+
+       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;
+}
+
+static void lpbtest_exit(void)
+{
+       platform_device_unregister(lpbtest_pdev);
+       platform_driver_unregister(&lpbtest_driver);
+}
+
+module_init(lpbtest_init);
+module_exit(lpbtest_exit);
-- 
1.6.5.5



-- 
Roman Fietze                Telemotive AG Büro Mühlhausen
Breitwiesen                              73347 Mühlhausen
Tel.: +49(0)7335/18493-45        http://www.telemotive.de
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to