Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:07

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*  Memory Scrubber register driver
0004  *
0005  *  COPYRIGHT (c) 2017.
0006  *  Cobham Gaisler AB.
0007  *
0008  * Redistribution and use in source and binary forms, with or without
0009  * modification, are permitted provided that the following conditions
0010  * are met:
0011  * 1. Redistributions of source code must retain the above copyright
0012  *    notice, this list of conditions and the following disclaimer.
0013  * 2. Redistributions in binary form must reproduce the above copyright
0014  *    notice, this list of conditions and the following disclaimer in the
0015  *    documentation and/or other materials provided with the distribution.
0016  *
0017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0027  * POSSIBILITY OF SUCH DAMAGE.
0028  */
0029 
0030 #include <stdint.h>
0031 #include <stdlib.h>
0032 #include <stdio.h>
0033 #include <string.h>
0034 #include <rtems/bspIo.h>
0035 #include <drvmgr/drvmgr.h>
0036 #include <grlib/ambapp_bus.h>
0037 
0038 #include <grlib/memscrub.h>
0039 
0040 /*#define STATIC*/
0041 #define STATIC static
0042 
0043 #define UNUSED __attribute__((unused))
0044 
0045 /*#define DEBUG 1*/
0046 
0047 #ifdef DEBUG
0048 #define DBG(x...) printf(x)
0049 #else
0050 #define DBG(x...) 
0051 #endif
0052 
0053 #define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
0054 #define REG_READ(addr) (*(volatile uint32_t *)(addr))
0055 
0056 /* 
0057  * MEMORYSCRUBBER AHBS register fields
0058  * DEFINED IN HEADER FILE
0059  */
0060 
0061 /* 
0062  * MEMORYSCRUBBER AHBERC register fields
0063  * DEFINED IN HEADER FILE
0064  */
0065 
0066 /* 
0067  * MEMORYSCRUBBER STAT register fields
0068  * DEFINED IN HEADER FILE
0069  */
0070 
0071 /* 
0072  * MEMORYSCRUBBER CONFIG register fields
0073  * DEFINED IN HEADER FILE
0074  */
0075 
0076 /* 
0077  * MEMORYSCRUBBER ETHRES register fields
0078  * DEFINED IN HEADER FILE
0079  */
0080 
0081 /* MEMORYSCRUBBER Registers layout */
0082 struct memscrub_regs {
0083     volatile uint32_t ahbstatus; /* 0x00 */
0084     volatile uint32_t ahbfailing; /* 0x04 */
0085     volatile uint32_t ahberc; /* 0x08 */
0086     volatile uint32_t resv1; /* 0x0c */
0087     volatile uint32_t status; /* 0x10 */
0088     volatile uint32_t config; /* 0x14 */
0089     volatile uint32_t rangel; /* 0x18 */
0090     volatile uint32_t rangeh; /* 0x1c */
0091     volatile uint32_t pos; /* 0x20 */
0092     volatile uint32_t ethres; /* 0x24 */
0093     volatile uint32_t init; /* 0x28 */
0094     volatile uint32_t rangel2; /* 0x2c */
0095     volatile uint32_t rangeh2; /* 0x30 */
0096 };
0097 
0098 #define DEVNAME_LEN 10
0099 struct memscrub_priv {
0100     struct drvmgr_dev *dev;
0101     char devname[DEVNAME_LEN];
0102     struct memscrub_regs *regs;
0103     int minor;
0104     int burstlen;
0105     int blockmask;
0106     /* Cached error */
0107     uint32_t last_status;
0108     uint32_t last_address;
0109     /* User defined ISR */
0110     memscrub_isr_t isr;
0111     void *isr_arg;
0112 };
0113 static struct memscrub_priv * memscrubpriv = NULL;
0114 
0115 STATIC int memscrub_init2(struct drvmgr_dev *dev);
0116 STATIC int memscrub_init(struct memscrub_priv *priv);
0117 
0118 void memscrub_isr(void *arg);
0119 
0120 struct drvmgr_drv_ops memscrub_ops =
0121 {
0122     .init = {NULL, memscrub_init2, NULL, NULL},
0123     .remove = NULL,
0124     .info = NULL
0125 };
0126 
0127 struct amba_dev_id memscrub_ids[] =
0128 {
0129     {VENDOR_GAISLER, GAISLER_MEMSCRUB},
0130     {0, 0}      /* Mark end of table */
0131 };
0132 
0133 struct amba_drv_info memscrub_drv_info =
0134 {
0135     {
0136         DRVMGR_OBJ_DRV,         /* Driver */
0137         NULL,               /* Next driver */
0138         NULL,               /* Device list */
0139         DRIVER_AMBAPP_GAISLER_MEMSCRUB_ID,/* Driver ID */
0140         "MEMSCRUB_DRV",         /* Driver Name */
0141         DRVMGR_BUS_TYPE_AMBAPP,     /* Bus Type */
0142         &memscrub_ops,
0143         NULL,               /* Funcs */
0144         0,              /* No devices yet */
0145         sizeof(struct memscrub_priv),
0146     },
0147     &memscrub_ids[0]
0148 };
0149 
0150 void memscrub_register_drv (void)
0151 {
0152     drvmgr_drv_register(&memscrub_drv_info.general);
0153 }
0154 
0155 STATIC int memscrub_init(struct memscrub_priv *priv)
0156 {
0157     struct ambapp_ahb_info *ahb;
0158     struct amba_dev_info *ainfo = priv->dev->businfo;
0159     unsigned int tmp;
0160     int i,j;
0161 
0162     /* Get device information from AMBA PnP information */
0163     if (ainfo == NULL){
0164         return MEMSCRUB_ERR_ERROR;
0165     }
0166 
0167     /* Find MEMSCRUB core from Plug&Play information */
0168     ahb = ainfo->info.ahb_slv;
0169     priv->regs = (struct memscrub_regs *)ahb->start[0];
0170 
0171     DBG("MEMSCRUB regs 0x%08x\n", (unsigned int) priv->regs);
0172 
0173     /* Find MEMSCRUB capabilities */
0174     tmp = REG_READ(&priv->regs->status);
0175     i = (tmp & STAT_BURSTLEN) >> STAT_BURSTLEN_BIT;
0176     for (j=1; i>0; i--) j <<= 1;
0177     priv->burstlen = j;
0178 
0179 
0180     /* If scrubber is active, we cannot stop it to read blockmask value */
0181     if (tmp & STAT_ACTIVE){
0182         priv->blockmask = 0;
0183     }else{
0184         /* Detect block size in bytes and burst length */
0185         tmp = REG_READ(&priv->regs->rangeh);
0186         REG_WRITE(&priv->regs->rangeh, 0);
0187         priv->blockmask = REG_READ(&priv->regs->rangeh);
0188         REG_WRITE(&priv->regs->rangeh, tmp);
0189     }
0190 
0191     /* DEBUG print */
0192     DBG("MEMSCRUB with following capabilities:\n");
0193     DBG(" -Burstlength: %d\n", priv->burstlen); 
0194 
0195     return MEMSCRUB_ERR_OK;
0196 }
0197 
0198 STATIC int memscrub_init2(struct drvmgr_dev *dev)
0199 {
0200     struct memscrub_priv *priv = dev->priv;
0201 
0202     DBG("MEMSCRUB[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
0203 
0204     if (memscrubpriv) {
0205         DBG("Driver only supports one MEMSCRUB core\n");
0206         return DRVMGR_FAIL;
0207     }
0208 
0209     if (priv==NULL){
0210         return DRVMGR_NOMEM;
0211     }
0212 
0213     /* Assign priv */
0214     priv->dev = dev;
0215     strncpy(&priv->devname[0], "memscrub0", DEVNAME_LEN);
0216     memscrubpriv=priv;
0217 
0218     /* Initilize driver struct */
0219     if (memscrub_init(priv) != MEMSCRUB_ERR_OK){
0220         return DRVMGR_FAIL;
0221     }
0222 
0223     /* Startup Action:
0224      *  - Clear status
0225      *  - Register ISR
0226      */
0227 
0228     /* Initialize hardware by clearing its status */
0229     REG_WRITE(&priv->regs->ahbstatus, 0);
0230     REG_WRITE(&priv->regs->status, 0);
0231 
0232     return DRVMGR_OK;
0233 }
0234 
0235 
0236 int memscrub_init_start(uint32_t value, uint8_t delay, int options)
0237 {
0238     struct memscrub_priv *priv = memscrubpriv;
0239     uint32_t sts, tmp;
0240     int i;
0241 
0242     if (priv==NULL){
0243         DBG("MEMSCRUB not init.\n");
0244         return MEMSCRUB_ERR_ERROR;
0245     }
0246 
0247     /* Check if scrubber is active */
0248     sts = REG_READ(&priv->regs->status);
0249     if (sts & STAT_ACTIVE){
0250         DBG("MEMSCRUB running.\n");
0251         return MEMSCRUB_ERR_ERROR;
0252     }
0253 
0254     /* Check if we need to probe blockmask */
0255     if (priv->blockmask == 0){
0256         /* Detect block size in bytes and burst length */
0257         tmp = REG_READ(&priv->regs->rangeh);
0258         REG_WRITE(&priv->regs->rangeh, 0);
0259         priv->blockmask = REG_READ(&priv->regs->rangeh);
0260         REG_WRITE(&priv->regs->rangeh, tmp);
0261     }
0262 
0263     /* Set data value */
0264     for (i=0; i<priv->blockmask; i+=4){
0265         REG_WRITE(&priv->regs->init,value);
0266     }
0267 
0268     /* Clear unused bits */
0269     options = options & ~(CONFIG_MODE | CONFIG_DELAY);
0270 
0271     /* Enable scrubber */
0272     REG_WRITE(&priv->regs->config, options | 
0273             ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
0274             CONFIG_MODE_INIT | CONFIG_SCEN);
0275 
0276     DBG("MEMSCRUB INIT STARTED\n");
0277 
0278     return MEMSCRUB_ERR_OK;
0279 }
0280 
0281 int memscrub_scrub_start(uint8_t delay, int options)
0282 {
0283     struct memscrub_priv *priv = memscrubpriv;
0284     uint32_t ctrl,sts;
0285 
0286     if (priv==NULL){
0287         DBG("MEMSCRUB not init.\n");
0288         return MEMSCRUB_ERR_ERROR;
0289     }
0290 
0291     /* Check if scrubber is active */
0292     sts = REG_READ(&priv->regs->status);
0293     if (sts & STAT_ACTIVE){
0294         /* Check if mode is not init */
0295         ctrl = REG_READ(&priv->regs->config);
0296         if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
0297             DBG("MEMSCRUB init running.\n");
0298             return MEMSCRUB_ERR_ERROR;
0299         }
0300     }
0301 
0302     /* Clear unused bits */
0303     options = options & ~(CONFIG_MODE | CONFIG_DELAY);
0304 
0305     /* Enable scrubber */
0306     REG_WRITE(&priv->regs->config, options | 
0307             ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
0308             CONFIG_MODE_SCRUB | CONFIG_SCEN);
0309 
0310     DBG("MEMSCRUB SCRUB STARTED\n");
0311 
0312     return MEMSCRUB_ERR_OK;
0313 }
0314 
0315 int memscrub_regen_start(uint8_t delay, int options)
0316 {
0317     struct memscrub_priv *priv = memscrubpriv;
0318     uint32_t ctrl,sts;
0319 
0320     if (priv==NULL){
0321         DBG("MEMSCRUB not init.\n");
0322         return MEMSCRUB_ERR_ERROR;
0323     }
0324 
0325     /* Check if scrubber is active */
0326     sts = REG_READ(&priv->regs->status);
0327     if (sts & STAT_ACTIVE){
0328         /* Check if mode is not init */
0329         ctrl = REG_READ(&priv->regs->config);
0330         if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
0331             DBG("MEMSCRUB init running.\n");
0332             return MEMSCRUB_ERR_ERROR;
0333         }
0334     }
0335 
0336     /* Clear unused bits */
0337     options = options & ~(CONFIG_MODE | CONFIG_DELAY);
0338 
0339     /* Enable scrubber */
0340     REG_WRITE(&priv->regs->config, options | 
0341             ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) | 
0342             CONFIG_MODE_REGEN | CONFIG_SCEN);
0343 
0344     DBG("MEMSCRUB REGEN STARTED\n");
0345 
0346     return MEMSCRUB_ERR_OK;
0347 }
0348 
0349 int memscrub_stop(void)
0350 {
0351     struct memscrub_priv *priv = memscrubpriv;
0352 
0353     if (priv==NULL){
0354         DBG("MEMSCRUB not init.\n");
0355         return MEMSCRUB_ERR_ERROR;
0356     }
0357 
0358     /* Disable scrubber */
0359     REG_WRITE(&priv->regs->config, 0);
0360 
0361     /* Wait until finished */
0362     while(REG_READ(&priv->regs->status) & STAT_ACTIVE){};
0363 
0364     DBG("MEMSCRUB STOPPED\n");
0365 
0366     return MEMSCRUB_ERR_OK;
0367 }
0368 
0369 int memscrub_range_set(uint32_t start, uint32_t end)
0370 {
0371     struct memscrub_priv *priv = memscrubpriv;
0372 
0373     if (priv==NULL){
0374         DBG("MEMSCRUB not init.\n");
0375         return MEMSCRUB_ERR_ERROR;
0376     }
0377 
0378     if (end <= start){
0379         DBG("MEMSCRUB wrong address.\n");
0380         return MEMSCRUB_ERR_EINVAL;
0381     }
0382 
0383     /* Check if scrubber is active */
0384     if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
0385         DBG("MEMSCRUB running.\n");
0386         return MEMSCRUB_ERR_ERROR;
0387     }
0388 
0389     /* Set range */
0390     REG_WRITE(&priv->regs->rangel, start);
0391     REG_WRITE(&priv->regs->rangeh, end);
0392 
0393     DBG("MEMSCRUB range: 0x%08x-0x%08x\n",
0394             (unsigned int) start,
0395             (unsigned int) end);
0396 
0397     return MEMSCRUB_ERR_OK;
0398 }
0399 
0400 int memscrub_secondary_range_set(uint32_t start, uint32_t end)
0401 {
0402     struct memscrub_priv *priv = memscrubpriv;
0403 
0404     if (priv==NULL){
0405         DBG("MEMSCRUB not init.\n");
0406         return MEMSCRUB_ERR_ERROR;
0407     }
0408 
0409     if (end <= start){
0410         DBG("MEMSCRUB wrong address.\n");
0411         return MEMSCRUB_ERR_EINVAL;
0412     }
0413 
0414     /* Check if scrubber is active */
0415     if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
0416         DBG("MEMSCRUB running.\n");
0417         return MEMSCRUB_ERR_ERROR;
0418     }
0419 
0420     /* Set range */
0421     REG_WRITE(&priv->regs->rangel2, start);
0422     REG_WRITE(&priv->regs->rangeh2, end);
0423 
0424     DBG("MEMSCRUB 2nd range: 0x%08x-0x%08x\n",
0425             (unsigned int) start,
0426             (unsigned int) end);
0427 
0428     return MEMSCRUB_ERR_OK;
0429 }
0430 
0431 int memscrub_range_get(uint32_t * start, uint32_t * end)
0432 {
0433     struct memscrub_priv *priv = memscrubpriv;
0434 
0435     if (priv==NULL){
0436         DBG("MEMSCRUB not init.\n");
0437         return MEMSCRUB_ERR_ERROR;
0438     }
0439 
0440     if ((start==NULL) || (end == NULL)){
0441         DBG("MEMSCRUB wrong pointer.\n");
0442         return MEMSCRUB_ERR_EINVAL;
0443     }
0444 
0445     /* Get range */
0446     *start = REG_READ(&priv->regs->rangel);
0447     *end = REG_READ(&priv->regs->rangeh);
0448 
0449     return MEMSCRUB_ERR_OK;
0450 }
0451 
0452 int memscrub_secondary_range_get(uint32_t * start, uint32_t * end)
0453 {
0454     struct memscrub_priv *priv = memscrubpriv;
0455 
0456     if (priv==NULL){
0457         DBG("MEMSCRUB not init.\n");
0458         return MEMSCRUB_ERR_ERROR;
0459     }
0460 
0461     if ((start==NULL) || (end == NULL)){
0462         DBG("MEMSCRUB wrong pointer.\n");
0463         return MEMSCRUB_ERR_EINVAL;
0464     }
0465 
0466     /* Get range */
0467     *start = REG_READ(&priv->regs->rangel2);
0468     *end = REG_READ(&priv->regs->rangeh2);
0469 
0470     return MEMSCRUB_ERR_OK;
0471 }
0472 
0473 int memscrub_ahberror_setup(int uethres, int cethres, int options)
0474 {
0475     struct memscrub_priv *priv = memscrubpriv;
0476 
0477     if (priv==NULL){
0478         DBG("MEMSCRUB not init.\n");
0479         return MEMSCRUB_ERR_ERROR;
0480     }
0481 
0482     /* Set AHBERR */
0483     REG_WRITE(&priv->regs->ahberc, 
0484             ((cethres << AHBERC_CECNTT_BIT) & AHBERC_CECNTT) |
0485             ((uethres << AHBERC_UECNTT_BIT) & AHBERC_UECNTT) |
0486             (options & (AHBERC_CECTE | AHBERC_UECTE)));
0487 
0488     DBG("MEMSCRUB ahb err: UE[%d]:%s, CE[%d]:%s\n",
0489             (unsigned int) uethres,
0490             (options & AHBERC_UECTE)? "enabled":"disabled",
0491             (unsigned int) cethres,
0492             (options & AHBERC_CECTE)? "enabled":"disabled"
0493             );
0494 
0495     return MEMSCRUB_ERR_OK;
0496 }
0497 
0498 int memscrub_scruberror_setup(int blkthres, int runthres, int options)
0499 {
0500     struct memscrub_priv *priv = memscrubpriv;
0501 
0502     if (priv==NULL){
0503         DBG("MEMSCRUB not init.\n");
0504         return MEMSCRUB_ERR_ERROR;
0505     }
0506 
0507     /* Set ETHRES */
0508     REG_WRITE(&priv->regs->ethres, 
0509             ((blkthres << ETHRES_BECT_BIT) & ETHRES_BECT) |
0510             ((runthres << ETHRES_RECT_BIT) & ETHRES_RECT) |
0511             (options & (ETHRES_RECTE | ETHRES_BECTE)));
0512 
0513     DBG("MEMSCRUB scrub err: BLK[%d]:%s, RUN[%d]:%s\n",
0514             (unsigned int) blkthres,
0515             (options & ETHRES_BECTE)? "enabled":"disabled",
0516             (unsigned int) runthres,
0517             (options & ETHRES_RECTE)? "enabled":"disabled"
0518             );
0519 
0520     return MEMSCRUB_ERR_OK;
0521 }
0522 
0523 int memscrub_scrub_position(uint32_t * position)
0524 {
0525     struct memscrub_priv *priv = memscrubpriv;
0526 
0527     if (priv==NULL){
0528         DBG("MEMSCRUB not init.\n");
0529         return MEMSCRUB_ERR_ERROR;
0530     }
0531 
0532     if (position==NULL){
0533         DBG("MEMSCRUB wrong pointer.\n");
0534         return MEMSCRUB_ERR_EINVAL;
0535     }
0536 
0537     *position = REG_READ(&priv->regs->pos);
0538 
0539     return MEMSCRUB_ERR_OK;
0540 }
0541 
0542 int memscrub_isr_register(memscrub_isr_t isr, void * data)
0543 {
0544     struct memscrub_priv *priv = memscrubpriv;
0545     unsigned int ethres, ahberc, config;
0546 
0547     if (priv==NULL){
0548         DBG("MEMSCRUB not init.\n");
0549         return MEMSCRUB_ERR_ERROR;
0550     }
0551 
0552     if (isr==NULL){
0553         DBG("MEMSCRUB wrong pointer.\n");
0554         return MEMSCRUB_ERR_EINVAL;
0555     }
0556 
0557     /* Mask interrupts */
0558     ethres = REG_READ(&priv->regs->ethres);
0559     REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
0560 
0561     ahberc = REG_READ(&priv->regs->ahberc);
0562     REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
0563 
0564     config = REG_READ(&priv->regs->config);
0565     REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
0566 
0567     /* Install IRQ handler if needed */
0568     if (priv->isr == NULL){
0569         drvmgr_interrupt_register(priv->dev, 0, priv->devname, memscrub_isr,
0570                 priv);
0571     }
0572 
0573     /* Install user ISR */
0574     priv->isr=isr;
0575     priv->isr_arg=data;
0576 
0577     /* Unmask interrupts */
0578     REG_WRITE(&priv->regs->ethres, ethres);
0579 
0580     REG_WRITE(&priv->regs->ahberc, ahberc);
0581 
0582     REG_WRITE(&priv->regs->config, config);
0583 
0584     return MEMSCRUB_ERR_OK;
0585 }
0586 
0587 int memscrub_isr_unregister(void)
0588 {
0589     struct memscrub_priv *priv = memscrubpriv;
0590     unsigned int ethres, ahberc, config;
0591 
0592     if (priv==NULL){
0593         DBG("MEMSCRUB not init.\n");
0594         return MEMSCRUB_ERR_ERROR;
0595     }
0596 
0597     if (priv->isr==NULL){
0598         DBG("MEMSCRUB wrong pointer.\n");
0599         return MEMSCRUB_ERR_EINVAL;
0600     }
0601 
0602     /* Mask interrupts */
0603     ethres = REG_READ(&priv->regs->ethres);
0604     REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
0605 
0606     ahberc = REG_READ(&priv->regs->ahberc);
0607     REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
0608 
0609     config = REG_READ(&priv->regs->config);
0610     REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
0611 
0612     /* Uninstall IRQ handler if needed */
0613     drvmgr_interrupt_unregister(priv->dev, 0, memscrub_isr, priv);
0614 
0615     /* Uninstall user ISR */
0616     priv->isr=NULL;
0617     priv->isr_arg=NULL;
0618 
0619     return MEMSCRUB_ERR_OK;
0620 }
0621 
0622 int memscrub_error_status(uint32_t *ahbaccess, uint32_t *ahbstatus, 
0623         uint32_t *scrubstatus)
0624 {
0625     struct memscrub_priv *priv = memscrubpriv;
0626     uint32_t mask, ahbstatus_val;
0627 
0628     if (priv==NULL){
0629         DBG("MEMSCRUB not init.\n");
0630         return MEMSCRUB_ERR_ERROR;
0631     }
0632 
0633     if ((ahbaccess==NULL) || (ahbstatus==NULL) || (scrubstatus == NULL)){
0634         DBG("MEMSCRUB wrong pointer.\n");
0635         return MEMSCRUB_ERR_EINVAL;
0636     }
0637 
0638     /* Get hardware status */
0639     *ahbaccess = REG_READ(&priv->regs->ahbfailing);
0640     *ahbstatus = ahbstatus_val = REG_READ(&priv->regs->ahbstatus);
0641     *scrubstatus = REG_READ(&priv->regs->status);
0642 
0643     /* Clear error status */
0644     mask = 0;
0645     /* Clear CECNT only if we crossed the CE threshold*/
0646     if ((ahbstatus_val & AHBS_CE) == 0){
0647         /* Don't clear the CECNT */
0648         mask |= AHBS_CECNT;
0649     }
0650     /* Clear UECNT only if we crossed the UE threshold*/
0651     if ((ahbstatus_val & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
0652         /* Don't clear the UECNT */
0653         mask |= AHBS_UECNT;
0654     }
0655     REG_WRITE(&priv->regs->ahbstatus, ahbstatus_val & mask);
0656     REG_WRITE(&priv->regs->status,0);
0657 
0658     return MEMSCRUB_ERR_OK;
0659 }
0660 
0661 int memscrub_active(void)
0662 {
0663     struct memscrub_priv *priv = memscrubpriv;
0664 
0665     if (priv==NULL){
0666         DBG("MEMSCRUB not init.\n");
0667         return MEMSCRUB_ERR_ERROR;
0668     }
0669 
0670     return REG_READ(&priv->regs->status) & STAT_ACTIVE;
0671 }
0672 
0673 void memscrub_isr(void *arg)
0674 {
0675     struct memscrub_priv *priv = arg;
0676     uint32_t fadr, ahbstatus, status, mask;
0677 
0678     /* Get hardware status */
0679     ahbstatus = REG_READ(&priv->regs->ahbstatus);
0680     if ((ahbstatus & (AHBS_NE|AHBS_DONE)) == 0){
0681         return;
0682     }
0683 
0684     /* IRQ generated by MEMSCRUB core... handle it here */
0685 
0686     /* Get Failing address */
0687     fadr = REG_READ(&priv->regs->ahbfailing);
0688 
0689     /* Get Status */
0690     status = REG_READ(&priv->regs->status);
0691 
0692     /* Clear error status */
0693     mask = 0;
0694     /* Clear CECNT only if we crossed the CE threshold*/
0695     if ((ahbstatus & AHBS_CE) == 0){
0696         /* Don't clear the CECNT */
0697         mask |= AHBS_CECNT;
0698     }
0699     /* Clear UECNT only if we crossed the UE threshold*/
0700     if ((ahbstatus & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
0701         /* Don't clear the UECNT */
0702         mask |= AHBS_UECNT;
0703     }
0704     REG_WRITE(&priv->regs->ahbstatus, ahbstatus & mask);
0705     REG_WRITE(&priv->regs->status,0);
0706 
0707     /* Let user handle error */
0708     (priv->isr)(priv->isr_arg, fadr, ahbstatus, status);
0709 
0710     return;
0711 }