From: Javier Jalle <javier.ja...@gaisler.com>

---
 c/src/lib/libbsp/sparc/Makefile.am                 |   4 +
 c/src/lib/libbsp/sparc/leon2/Makefile.am           |   4 +
 c/src/lib/libbsp/sparc/leon2/preinstall.am         |   4 +
 c/src/lib/libbsp/sparc/leon3/Makefile.am           |   4 +
 c/src/lib/libbsp/sparc/leon3/preinstall.am         |   4 +
 .../sparc/shared/include/drvmgr/ambapp_bus.h       |   1 +
 c/src/lib/libbsp/sparc/shared/include/memscrub.h   | 172 +++++
 c/src/lib/libbsp/sparc/shared/scrub/memscrub.c     | 691 +++++++++++++++++++++
 cpukit/libdrvmgr/drvmgr_confdefs.h                 |   4 +
 9 files changed, 888 insertions(+)
 create mode 100644 c/src/lib/libbsp/sparc/shared/include/memscrub.h
 create mode 100644 c/src/lib/libbsp/sparc/shared/scrub/memscrub.c
diff --git a/c/src/lib/libbsp/sparc/Makefile.am 
b/c/src/lib/libbsp/sparc/Makefile.am
index 2535752..84056ed 100644
--- a/c/src/lib/libbsp/sparc/Makefile.am
+++ b/c/src/lib/libbsp/sparc/Makefile.am
@@ -182,5 +182,9 @@ EXTRA_DIST += shared/include/grtc.h
 EXTRA_DIST += shared/tmtc/grtm.c
 EXTRA_DIST += shared/include/grtm.h
 
+# MEMSCRUB
+EXTRA_DIST += shared/stat/memscrub.c
+EXTRA_DIST += shared/include/memscrub.h
+
 include $(top_srcdir)/../../../automake/subdirs.am
 include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/lib/libbsp/sparc/leon2/Makefile.am 
b/c/src/lib/libbsp/sparc/leon2/Makefile.am
index 9078cf5..6106f03 100644
--- a/c/src/lib/libbsp/sparc/leon2/Makefile.am
+++ b/c/src/lib/libbsp/sparc/leon2/Makefile.am
@@ -205,6 +205,10 @@ include_bsp_HEADERS += ../../sparc/shared/include/grtm.h
 libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtc.c
 libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c
 
+# MEMSCRUB
+libbsp_a_SOURCES += ../../sparc/shared/scrub/memscrub.c
+include_bsp_HEADERS += ../../sparc/shared/include/memscrub.h
+
 # Driver Manager
 include_drvmgrdir = $(includedir)/drvmgr
 include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus.h
diff --git a/c/src/lib/libbsp/sparc/leon2/preinstall.am 
b/c/src/lib/libbsp/sparc/leon2/preinstall.am
index 2aad5f9..eae20e7 100644
--- a/c/src/lib/libbsp/sparc/leon2/preinstall.am
+++ b/c/src/lib/libbsp/sparc/leon2/preinstall.am
@@ -269,6 +269,10 @@ $(PROJECT_INCLUDE)/bsp/grtm.h: 
../../sparc/shared/include/grtm.h $(PROJECT_INCLU
        $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/grtm.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/grtm.h
 
+$(PROJECT_INCLUDE)/bsp/memscrub.h: ../../sparc/shared/include/memscrub.h 
$(PROJECT_INCLUDE)/bsp/$(dirstamp)
+       $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/memscrub.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/memscrub.h
+
 $(PROJECT_INCLUDE)/drvmgr/$(dirstamp):
        @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr
        @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am 
b/c/src/lib/libbsp/sparc/leon3/Makefile.am
index 3386cf0..d8db46a 100644
--- a/c/src/lib/libbsp/sparc/leon3/Makefile.am
+++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am
@@ -239,6 +239,10 @@ include_bsp_HEADERS += ../../sparc/shared/include/grtm.h
 libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtc.c
 libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c
 
+# MEMSCRUB
+libbsp_a_SOURCES += ../../sparc/shared/scrub/memscrub.c
+include_bsp_HEADERS += ../../sparc/shared/include/memscrub.h
+
 # Driver Manager 
 include_drvmgrdir = $(includedir)/drvmgr
 include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus_grlib.h
diff --git a/c/src/lib/libbsp/sparc/leon3/preinstall.am 
b/c/src/lib/libbsp/sparc/leon3/preinstall.am
index b8ac694..bc709a7 100644
--- a/c/src/lib/libbsp/sparc/leon3/preinstall.am
+++ b/c/src/lib/libbsp/sparc/leon3/preinstall.am
@@ -317,6 +317,10 @@ $(PROJECT_INCLUDE)/bsp/grtm.h: 
../../sparc/shared/include/grtm.h $(PROJECT_INCLU
        $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/grtm.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/grtm.h
 
+$(PROJECT_INCLUDE)/bsp/memscrub.h: ../../sparc/shared/include/memscrub.h 
$(PROJECT_INCLUDE)/bsp/$(dirstamp)
+       $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/memscrub.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/memscrub.h
+
 $(PROJECT_INCLUDE)/drvmgr/$(dirstamp):
        @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr
        @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h 
b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h
index 203e7e9..41df26e 100644
--- a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h
+++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h
@@ -57,6 +57,7 @@ extern "C" {
 #define DRIVER_AMBAPP_GAISLER_SPWCUC_ID                
DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPWCUC)
 #define DRIVER_AMBAPP_GAISLER_SPW_ROUTER_ID    
DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW_ROUTER)
 #define DRIVER_AMBAPP_GAISLER_L2CACHE_ID       
DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_L2CACHE)
+#define DRIVER_AMBAPP_GAISLER_MEMSCRUB_ID      
DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_MEMSCRUB)
 
 /*** ESA Hardware Device Driver IDs ***/
 #define DRIVER_AMBAPP_ESA_MCTRL_ID             DRIVER_AMBAPP_ID(VENDOR_ESA, 
ESA_MCTRL)
diff --git a/c/src/lib/libbsp/sparc/shared/include/memscrub.h 
b/c/src/lib/libbsp/sparc/shared/include/memscrub.h
new file mode 100644
index 0000000..1e55d8e
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/include/memscrub.h
@@ -0,0 +1,172 @@
+/*  MEMSCRUB driver interface
+ *
+ *  COPYRIGHT (c) 2017.
+ *  Cobham Gaisler AB.
+ *
+ *  The license and distribution terms for this file may be
+ *  found in the file LICENSE in this distribution or at
+ *  http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef __MEMSCRUB_H__
+#define __MEMSCRUB_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MEMSCRUB_ERR_OK 0
+#define MEMSCRUB_ERR_EINVAL -1
+#define MEMSCRUB_ERR_ERROR -2
+
+extern void memscrub_register_drv(void);
+
+/* 
+ * MEMORYSCRUBBER CONFIG register fields
+ */
+#define CONFIG_DELAY_BIT 8
+#define CONFIG_IRQD_BIT 7
+#define CONFIG_SERA_BIT 5
+#define CONFIG_LOOP_BIT 4
+#define CONFIG_MODE_BIT 2
+#define CONFIG_ES_BIT 1
+#define CONFIG_SCEN_BIT 0
+
+#define CONFIG_DELAY (0xff << CONFIG_DELAY_BIT)
+#define CONFIG_IRQD (0x1 << CONFIG_IRQD_BIT)
+#define CONFIG_SERA (0x1 << CONFIG_SERA_BIT)
+#define CONFIG_LOOP (0x1 << CONFIG_LOOP_BIT)
+#define CONFIG_MODE (0x3 << CONFIG_MODE_BIT)
+#define CONFIG_ES (0x1 << CONFIG_ES_BIT)
+#define CONFIG_SCEN (0x1 << CONFIG_SCEN_BIT)
+#define CONFIG_MODE_SCRUB (0x0 << CONFIG_MODE_BIT)
+#define CONFIG_MODE_REGEN (0x1 << CONFIG_MODE_BIT)
+#define CONFIG_MODE_INIT (0x2 << CONFIG_MODE_BIT)
+
+#define MEMSCRUB_OPTIONS_INTERRUPTDONE_ENABLE CONFIG_IRQD
+#define MEMSCRUB_OPTIONS_INTERRUPTDONE_DISABLE 0
+#define MEMSCRUB_OPTIONS_EXTERNALSTART_ENABLE CONFIG_ES
+#define MEMSCRUB_OPTIONS_EXTERNALSTART_DISABLE 0
+#define MEMSCRUB_OPTIONS_LOOPMODE_ENABLE CONFIG_LOOP
+#define MEMSCRUB_OPTIONS_LOOPMODE_DISABLE 0
+#define MEMSCRUB_OPTIONS_SECONDARY_MEMRANGE_ENABLE CONFIG_SERA
+#define MEMSCRUB_OPTIONS_SECONDARY_MEMRANGE_DISABLE 0
+
+/* Scrubbing modes */
+extern int memscrub_init_start(uint32_t value, uint8_t delay, int options);
+extern int memscrub_scrub_start(uint8_t delay, int options);
+extern int memscrub_regen_start(uint8_t delay, int options);
+extern int memscrub_stop(void);
+extern int memscrub_active(void);
+
+/* Set/get memory ranges */
+extern int memscrub_range_set(uint32_t start, uint32_t end);
+extern int memscrub_range_get(uint32_t * start, uint32_t * end);
+extern int memscrub_secondary_range_set(uint32_t start, uint32_t end);
+extern int memscrub_secondary_range_get(uint32_t * start, uint32_t * end);
+
+/* Interrupts */
+/* MEMSCRUB Interrupts */
+/* Function Interrupt-Code ISR callback prototype.
+ * arg    - Custom arg provided by user
+ * access  - AHB Access that failed
+ * ahbstatus  - AHB status register of the MEMSCRUB core
+ * status  - status register of the MEMSCRUB core
+ */
+typedef void (*memscrub_isr_t)(void *arg, uint32_t ahbaccess, 
+               uint32_t ahbstatus, uint32_t scrubstatus);
+extern int memscrub_isr_register(memscrub_isr_t isr, void * data);
+extern int memscrub_isr_unregister(void);
+
+extern int memscrub_error_status(uint32_t *ahbaccess, uint32_t *ahbstatus, 
+               uint32_t *scrubstatus);
+
+/* Set the different error thresholds. */
+
+/* 
+ * MEMORYSCRUBBER AHBS register fields
+ */
+#define AHBS_CECNT_BIT 22
+#define AHBS_UECNT_BIT 14
+#define AHBS_DONE_BIT 13
+#define AHBS_SEC_BIT 11
+#define AHBS_SBC_BIT 10
+#define AHBS_CE_BIT 9
+#define AHBS_NE_BIT 8
+#define AHBS_HW_BIT 7
+#define AHBS_HM_BIT 3
+#define AHBS_HS_BIT 0
+
+#define AHBS_CECNT (0x3ff << AHBS_CECNT_BIT)
+#define AHBS_UECNT (0xff << AHBS_UECNT_BIT)
+#define AHBS_DONE (1 << AHBS_DONE_BIT)
+#define AHBS_SEC (1 << AHBS_SEC_BIT)
+#define AHBS_SBC (1 << AHBS_SBC_BIT)
+#define AHBS_CE (1 << AHBS_CE_BIT)
+#define AHBS_NE (1 << AHBS_NE_BIT)
+#define AHBS_HW (1 << AHBS_HW_BIT)
+#define AHBS_HM (0xf << AHBS_HM_BIT)
+#define AHBS_HS (0x7 << AHBS_HS_BIT)
+
+/* 
+ * MEMORYSCRUBBER STAT register fields
+ */
+#define STAT_RUNCOUNT_BIT 22
+#define STAT_BLKCOUNT_BIT 14
+#define STAT_DONE_BIT 13
+#define STAT_BURSTLEN_BIT 1
+#define STAT_ACTIVE_BIT 0
+
+#define STAT_RUNCOUNT (0x3ff << STAT_RUNCOUNT_BIT)
+#define STAT_BLKCOUNT (0xff << STAT_BLKCOUNT_BIT)
+#define STAT_DONE (0x1 << STAT_DONE_BIT)
+#define STAT_BURSTLEN (0xf << STAT_BURSTLEN_BIT)
+#define STAT_ACTIVE (0x1 << STAT_ACTIVE_BIT)
+
+/* 
+ * MEMORYSCRUBBER AHBERC register fields
+ */
+#define AHBERC_CECNTT_BIT 22
+#define AHBERC_UECNTT_BIT 14
+#define AHBERC_CECTE_BIT 1
+#define AHBERC_UECTE_BIT 0
+
+#define AHBERC_CECNTT (0x3ff << AHBERC_CECNTT_BIT)
+#define AHBERC_UECNTT (0xff << AHBERC_UECNTT_BIT)
+#define AHBERC_CECTE (0x1 << AHBERC_CECTE_BIT)
+#define AHBERC_UECTE (0x1 << AHBERC_UECTE_BIT)
+
+/* 
+ * MEMORYSCRUBBER ETHRES register fields
+ */
+#define ETHRES_RECT_BIT 22
+#define ETHRES_BECT_BIT 14
+#define ETHRES_RECTE_BIT 1
+#define ETHRES_BECTE_BIT 0
+
+#define ETHRES_RECT (0x3ff << ETHRES_RECT_BIT)
+#define ETHRES_BECT (0xff << ETHRES_BECT_BIT)
+#define ETHRES_RECTE (0x1 << ETHRES_RECTE_BIT)
+#define ETHRES_BECTE (0x1 << ETHRES_BECTE_BIT)
+
+#define MEMSCRUB_OPTIONS_AHBERROR_CORTHRES_ENABLE AHBERC_CECTE
+#define MEMSCRUB_OPTIONS_AHBERROR_CORTHRES_DISABLE 0
+#define MEMSCRUB_OPTIONS_AHBERROR_UNCORTHRES_ENABLE AHBERC_UECTE
+#define MEMSCRUB_OPTIONS_AHBERROR_UNCORTHRES_DISABLE 0
+#define MEMSCRUB_OPTIONS_SCRUBERROR_RUNTHRES_ENABLE ETHRES_RECTE
+#define MEMSCRUB_OPTIONS_SCRUBERROR_RUNTHRES_DISABLE 0
+#define MEMSCRUB_OPTIONS_SCRUBERROR_BLOCKTHRES_ENABLE ETHRES_BECTE
+#define MEMSCRUB_OPTIONS_SCRUBERROR_BLOCKTHRES_DISABLE 0
+extern int memscrub_ahberror_setup(int uethres, int cethres, int options);
+extern int memscrub_scruberror_setup(int blkthres, int runthres, 
+               int options);
+
+extern int memscrub_scrub_position(uint32_t * position); 
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/lib/libbsp/sparc/shared/scrub/memscrub.c 
b/c/src/lib/libbsp/sparc/shared/scrub/memscrub.c
new file mode 100644
index 0000000..cf02689
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/scrub/memscrub.c
@@ -0,0 +1,691 @@
+/*  Memory Scrubber register driver
+ *
+ *  COPYRIGHT (c) 2017.
+ *  Cobham Gaisler AB.
+ *
+ *  The license and distribution terms for this file may be
+ *  found in the file LICENSE in this distribution or at
+ *  http://www.rtems.org/license/LICENSE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <rtems/bspIo.h>
+#include <drvmgr/drvmgr.h>
+#include <drvmgr/ambapp_bus.h>
+
+#include <bsp/memscrub.h>
+
+/*#define STATIC*/
+#define STATIC static
+
+#define UNUSED __attribute__((unused))
+
+/*#define DEBUG 1*/
+
+#ifdef DEBUG
+#define DBG(x...) printf(x)
+#else
+#define DBG(x...) 
+#endif
+
+#define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
+#define REG_READ(addr) (*(volatile uint32_t *)(addr))
+
+/* 
+ * MEMORYSCRUBBER AHBS register fields
+ * DEFINED IN HEADER FILE
+ */
+
+/* 
+ * MEMORYSCRUBBER AHBERC register fields
+ * DEFINED IN HEADER FILE
+ */
+
+/* 
+ * MEMORYSCRUBBER STAT register fields
+ * DEFINED IN HEADER FILE
+ */
+
+/* 
+ * MEMORYSCRUBBER CONFIG register fields
+ * DEFINED IN HEADER FILE
+ */
+
+/* 
+ * MEMORYSCRUBBER ETHRES register fields
+ * DEFINED IN HEADER FILE
+ */
+
+/* MEMORYSCRUBBER Registers layout */
+struct memscrub_regs {
+       volatile uint32_t ahbstatus; /* 0x00 */
+       volatile uint32_t ahbfailing; /* 0x04 */
+       volatile uint32_t ahberc; /* 0x08 */
+       volatile uint32_t resv1; /* 0x0c */
+       volatile uint32_t status; /* 0x10 */
+       volatile uint32_t config; /* 0x14 */
+       volatile uint32_t rangel; /* 0x18 */
+       volatile uint32_t rangeh; /* 0x1c */
+       volatile uint32_t pos; /* 0x20 */
+       volatile uint32_t ethres; /* 0x24 */
+       volatile uint32_t init; /* 0x28 */
+       volatile uint32_t rangel2; /* 0x2c */
+       volatile uint32_t rangeh2; /* 0x30 */
+};
+
+#define DEVNAME_LEN 10
+struct memscrub_priv {
+       struct drvmgr_dev *dev;
+       char devname[DEVNAME_LEN];
+       struct memscrub_regs *regs;
+       int minor;
+       int burstlen;
+       int blockmask;
+       /* Cached error */
+       uint32_t last_status;
+       uint32_t last_address;
+       /* User defined ISR */
+       memscrub_isr_t isr;
+       void *isr_arg;
+};
+static struct memscrub_priv * memscrubpriv = NULL;
+
+STATIC int memscrub_init2(struct drvmgr_dev *dev);
+STATIC int memscrub_init(struct memscrub_priv *priv);
+
+void memscrub_isr(void *arg);
+
+struct drvmgr_drv_ops memscrub_ops =
+{
+       .init = {NULL, memscrub_init2, NULL, NULL},
+       .remove = NULL,
+       .info = NULL
+};
+
+struct amba_dev_id memscrub_ids[] =
+{
+       {VENDOR_GAISLER, GAISLER_MEMSCRUB},
+       {0, 0}          /* Mark end of table */
+};
+
+struct amba_drv_info memscrub_drv_info =
+{
+       {
+               DRVMGR_OBJ_DRV,                 /* Driver */
+               NULL,                           /* Next driver */
+               NULL,                           /* Device list */
+               DRIVER_AMBAPP_GAISLER_MEMSCRUB_ID,/* Driver ID */
+               "MEMSCRUB_DRV",                 /* Driver Name */
+               DRVMGR_BUS_TYPE_AMBAPP,         /* Bus Type */
+               &memscrub_ops,
+               NULL,                           /* Funcs */
+               0,                              /* No devices yet */
+               sizeof(struct memscrub_priv),
+       },
+       &memscrub_ids[0]
+};
+
+void memscrub_register_drv (void)
+{
+       drvmgr_drv_register(&memscrub_drv_info.general);
+}
+
+STATIC int memscrub_init(struct memscrub_priv *priv)
+{
+       struct ambapp_ahb_info *ahb;
+       struct amba_dev_info *ainfo = priv->dev->businfo;
+       unsigned int tmp;
+       int i,j;
+
+       /* Get device information from AMBA PnP information */
+       if (ainfo == NULL){
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Find MEMSCRUB core from Plug&Play information */
+       ahb = ainfo->info.ahb_slv;
+       priv->regs = (struct memscrub_regs *)ahb->start[0];
+
+       DBG("MEMSCRUB regs 0x%08x\n", (unsigned int) priv->regs);
+
+       /* Find MEMSCRUB capabilities */
+       tmp = REG_READ(&priv->regs->status);
+       i = (tmp & STAT_BURSTLEN) >> STAT_BURSTLEN_BIT;
+       for (j=1; i>0; i--) j <<= 1;
+       priv->burstlen = j;
+
+
+       /* If scrubber is active, we cannot stop it to read blockmask value */
+       if (tmp & STAT_ACTIVE){
+               priv->blockmask = 0;
+       }else{
+               /* Detect block size in bytes and burst length */
+               tmp = REG_READ(&priv->regs->rangeh);
+               REG_WRITE(&priv->regs->rangeh, 0);
+               priv->blockmask = REG_READ(&priv->regs->rangeh);
+               REG_WRITE(&priv->regs->rangeh, tmp);
+       }
+
+       /* DEBUG print */
+       DBG("MEMSCRUB with following capabilities:\n");
+       DBG(" -Burstlength: %d\n", priv->burstlen); 
+
+       return MEMSCRUB_ERR_OK;
+}
+
+STATIC int memscrub_init2(struct drvmgr_dev *dev)
+{
+       struct memscrub_priv *priv = dev->priv;
+
+       DBG("MEMSCRUB[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
+
+       if (memscrubpriv) {
+               DBG("Driver only supports one MEMSCRUB core\n");
+               return DRVMGR_FAIL;
+       }
+
+       if (priv==NULL){
+               return DRVMGR_NOMEM;
+       }
+
+       /* Assign priv */
+       priv->dev = dev;
+       strncpy(&priv->devname[0], "memscrub0", DEVNAME_LEN);
+       memscrubpriv=priv;
+
+       /* Initilize driver struct */
+       if (memscrub_init(priv) != MEMSCRUB_ERR_OK){
+               return DRVMGR_FAIL;
+       }
+
+       /* Startup Action:
+        *      - Clear status
+        *      - Register ISR
+        */
+
+       /* Initialize hardware by clearing its status */
+       REG_WRITE(&priv->regs->ahbstatus, 0);
+       REG_WRITE(&priv->regs->status, 0);
+
+       return DRVMGR_OK;
+}
+
+
+int memscrub_init_start(uint32_t value, uint8_t delay, int options)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       uint32_t sts, tmp;
+       int i;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Check if scrubber is active */
+       sts = REG_READ(&priv->regs->status);
+       if (sts & STAT_ACTIVE){
+               DBG("MEMSCRUB running.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Check if we need to probe blockmask */
+       if (priv->blockmask == 0){
+               /* Detect block size in bytes and burst length */
+               tmp = REG_READ(&priv->regs->rangeh);
+               REG_WRITE(&priv->regs->rangeh, 0);
+               priv->blockmask = REG_READ(&priv->regs->rangeh);
+               REG_WRITE(&priv->regs->rangeh, tmp);
+       }
+
+       /* Set data value */
+       for (i=0; i<priv->blockmask; i+=4){
+               REG_WRITE(&priv->regs->init,value);
+       }
+
+       /* Clear unused bits */
+       options = options & ~(CONFIG_MODE | CONFIG_DELAY);
+
+       /* Enable scrubber */
+       REG_WRITE(&priv->regs->config, options | 
+                       ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
+                       CONFIG_MODE_INIT | CONFIG_SCEN);
+
+       DBG("MEMSCRUB INIT STARTED\n");
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_scrub_start(uint8_t delay, int options)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       uint32_t ctrl,sts;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Check if scrubber is active */
+       sts = REG_READ(&priv->regs->status);
+       if (sts & STAT_ACTIVE){
+               /* Check if mode is not init */
+               ctrl = REG_READ(&priv->regs->config);
+               if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
+                       DBG("MEMSCRUB init running.\n");
+                       return MEMSCRUB_ERR_ERROR;
+               }
+       }
+
+       /* Clear unused bits */
+       options = options & ~(CONFIG_MODE | CONFIG_DELAY);
+
+       /* Enable scrubber */
+       REG_WRITE(&priv->regs->config, options | 
+                       ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
+                       CONFIG_MODE_SCRUB | CONFIG_SCEN);
+
+       DBG("MEMSCRUB SCRUB STARTED\n");
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_regen_start(uint8_t delay, int options)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       uint32_t ctrl,sts;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Check if scrubber is active */
+       sts = REG_READ(&priv->regs->status);
+       if (sts & STAT_ACTIVE){
+               /* Check if mode is not init */
+               ctrl = REG_READ(&priv->regs->config);
+               if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
+                       DBG("MEMSCRUB init running.\n");
+                       return MEMSCRUB_ERR_ERROR;
+               }
+       }
+
+       /* Clear unused bits */
+       options = options & ~(CONFIG_MODE | CONFIG_DELAY);
+
+       /* Enable scrubber */
+       REG_WRITE(&priv->regs->config, options | 
+                       ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
+                       CONFIG_MODE_REGEN | CONFIG_SCEN);
+
+       DBG("MEMSCRUB REGEN STARTED\n");
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_stop(void)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Disable scrubber */
+       REG_WRITE(&priv->regs->config, 0);
+
+       /* Wait until finished */
+       while(REG_READ(&priv->regs->status) & STAT_ACTIVE){};
+
+       DBG("MEMSCRUB STOPPED\n");
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_range_set(uint32_t start, uint32_t end)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if (end <= start){
+               DBG("MEMSCRUB wrong address.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Check if scrubber is active */
+       if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
+               DBG("MEMSCRUB running.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Set range */
+       REG_WRITE(&priv->regs->rangel, start);
+       REG_WRITE(&priv->regs->rangeh, end);
+
+       DBG("MEMSCRUB range: 0x%08x-0x%08x\n",
+                       (unsigned int) start,
+                       (unsigned int) end);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_secondary_range_set(uint32_t start, uint32_t end)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if (end <= start){
+               DBG("MEMSCRUB wrong address.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Check if scrubber is active */
+       if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
+               DBG("MEMSCRUB running.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Set range */
+       REG_WRITE(&priv->regs->rangel2, start);
+       REG_WRITE(&priv->regs->rangeh2, end);
+
+       DBG("MEMSCRUB 2nd range: 0x%08x-0x%08x\n",
+                       (unsigned int) start,
+                       (unsigned int) end);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_range_get(uint32_t * start, uint32_t * end)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if ((start==NULL) || (end == NULL)){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Get range */
+       *start = REG_READ(&priv->regs->rangel);
+       *end = REG_READ(&priv->regs->rangeh);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_secondary_range_get(uint32_t * start, uint32_t * end)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if ((start==NULL) || (end == NULL)){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Get range */
+       *start = REG_READ(&priv->regs->rangel2);
+       *end = REG_READ(&priv->regs->rangeh2);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_ahberror_setup(int uethres, int cethres, int options)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Set AHBERR */
+       REG_WRITE(&priv->regs->ahberc, 
+                       ((cethres << AHBERC_CECNTT_BIT) & AHBERC_CECNTT) |
+                       ((uethres << AHBERC_UECNTT_BIT) & AHBERC_UECNTT) |
+                       (options & (AHBERC_CECTE | AHBERC_UECTE)));
+
+       DBG("MEMSCRUB ahb err: UE[%d]:%s, CE[%d]:%s\n",
+                       (unsigned int) uethres,
+                       (options & AHBERC_UECTE)? "enabled":"disabled",
+                       (unsigned int) cethres,
+                       (options & AHBERC_CECTE)? "enabled":"disabled"
+                       );
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_scruberror_setup(int blkthres, int runthres, int options)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       /* Set ETHRES */
+       REG_WRITE(&priv->regs->ethres, 
+                       ((blkthres << ETHRES_BECT_BIT) & ETHRES_BECT) |
+                       ((runthres << ETHRES_RECT_BIT) & ETHRES_RECT) |
+                       (options & (ETHRES_RECTE | ETHRES_BECTE)));
+
+       DBG("MEMSCRUB scrub err: BLK[%d]:%s, RUN[%d]:%s\n",
+                       (unsigned int) blkthres,
+                       (options & ETHRES_BECTE)? "enabled":"disabled",
+                       (unsigned int) runthres,
+                       (options & ETHRES_RECTE)? "enabled":"disabled"
+                       );
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_scrub_position(uint32_t * position)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if (position==NULL){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       *position = REG_READ(&priv->regs->pos);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_isr_register(memscrub_isr_t isr, void * data)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       unsigned int ethres, ahberc, config;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if (isr==NULL){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Mask interrupts */
+       ethres = REG_READ(&priv->regs->ethres);
+       REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
+
+       ahberc = REG_READ(&priv->regs->ahberc);
+       REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
+
+       config = REG_READ(&priv->regs->config);
+       REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
+
+       /* Install IRQ handler if needed */
+       if (priv->isr == NULL){
+               drvmgr_interrupt_register(priv->dev, 0, priv->devname, 
memscrub_isr,
+                               priv);
+       }
+
+       /* Install user ISR */
+       priv->isr=isr;
+       priv->isr_arg=data;
+
+       /* Unmask interrupts */
+       REG_WRITE(&priv->regs->ethres, ethres);
+
+       REG_WRITE(&priv->regs->ahberc, ahberc);
+
+       REG_WRITE(&priv->regs->config, config);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_isr_unregister(void)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       unsigned int ethres, ahberc, config;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if (priv->isr==NULL){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Mask interrupts */
+       ethres = REG_READ(&priv->regs->ethres);
+       REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
+
+       ahberc = REG_READ(&priv->regs->ahberc);
+       REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
+
+       config = REG_READ(&priv->regs->config);
+       REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
+
+       /* Uninstall IRQ handler if needed */
+       drvmgr_interrupt_unregister(priv->dev, 0, memscrub_isr, priv);
+
+       /* Uninstall user ISR */
+       priv->isr=NULL;
+       priv->isr_arg=NULL;
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_error_status(uint32_t *ahbaccess, uint32_t *ahbstatus, 
+               uint32_t *scrubstatus)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+       uint32_t mask, ahbstatus_val;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       if ((ahbaccess==NULL) || (ahbstatus==NULL) || (scrubstatus == NULL)){
+               DBG("MEMSCRUB wrong pointer.\n");
+               return MEMSCRUB_ERR_EINVAL;
+       }
+
+       /* Get hardware status */
+       *ahbaccess = REG_READ(&priv->regs->ahbfailing);
+       *ahbstatus = ahbstatus_val = REG_READ(&priv->regs->ahbstatus);
+       *scrubstatus = REG_READ(&priv->regs->status);
+
+       /* Clear error status */
+       mask = 0;
+       /* Clear CECNT only if we crossed the CE threshold*/
+       if ((ahbstatus_val & AHBS_CE) == 0){
+               /* Don't clear the CECNT */
+               mask |= AHBS_CECNT;
+       }
+       /* Clear UECNT only if we crossed the UE threshold*/
+       if ((ahbstatus_val & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
+               /* Don't clear the UECNT */
+               mask |= AHBS_UECNT;
+       }
+       REG_WRITE(&priv->regs->ahbstatus, ahbstatus_val & mask);
+       REG_WRITE(&priv->regs->status,0);
+
+       return MEMSCRUB_ERR_OK;
+}
+
+int memscrub_active(void)
+{
+       struct memscrub_priv *priv = memscrubpriv;
+
+       if (priv==NULL){
+               DBG("MEMSCRUB not init.\n");
+               return MEMSCRUB_ERR_ERROR;
+       }
+
+       return REG_READ(&priv->regs->status) & STAT_ACTIVE;
+}
+
+void memscrub_isr(void *arg)
+{
+       struct memscrub_priv *priv = arg;
+       uint32_t fadr, ahbstatus, status, mask;
+
+       /* Get hardware status */
+       ahbstatus = REG_READ(&priv->regs->ahbstatus);
+       if ((ahbstatus & (AHBS_NE|AHBS_DONE)) == 0){
+               return;
+       }
+
+       /* IRQ generated by MEMSCRUB core... handle it here */
+
+       /* Get Failing address */
+       fadr = REG_READ(&priv->regs->ahbfailing);
+
+       /* Get Status */
+       status = REG_READ(&priv->regs->status);
+
+       /* Clear error status */
+       mask = 0;
+       /* Clear CECNT only if we crossed the CE threshold*/
+       if ((ahbstatus & AHBS_CE) == 0){
+               /* Don't clear the CECNT */
+               mask |= AHBS_CECNT;
+       }
+       /* Clear UECNT only if we crossed the UE threshold*/
+       if ((ahbstatus & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
+               /* Don't clear the UECNT */
+               mask |= AHBS_UECNT;
+       }
+       REG_WRITE(&priv->regs->ahbstatus, ahbstatus & mask);
+       REG_WRITE(&priv->regs->status,0);
+
+       /* Let user handle error */
+       (priv->isr)(priv->isr_arg, fadr, ahbstatus, status);
+
+       return;
+}
diff --git a/cpukit/libdrvmgr/drvmgr_confdefs.h 
b/cpukit/libdrvmgr/drvmgr_confdefs.h
index fd19139..34fc67e 100644
--- a/cpukit/libdrvmgr/drvmgr_confdefs.h
+++ b/cpukit/libdrvmgr/drvmgr_confdefs.h
@@ -59,6 +59,7 @@ extern void spwcuc_register(void);
 extern void grctm_register(void);
 extern void router_register_drv(void);
 extern void ahbstat_register_drv(void);
+extern void memscrub_register_drv(void);
 
 
 /*** LEON2 AMBA Hard coded bus Drivers ***/
@@ -170,6 +171,9 @@ drvmgr_drv_reg_func drvmgr_drivers[] = {
 #ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_AHBSTAT
        ahbstat_register_drv,
 #endif
+#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_MEMSCRUB
+       memscrub_register_drv,
+#endif
 
        /*** LEON2 AMBA Drivers ***/
 #ifdef CONFIGURE_DRIVER_LEON2_AT697PCI
-- 
2.7.4

_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to