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