source: rtems/c/src/lib/libbsp/sparc/shared/scrub/memscrub.c @ b2ed712

5
Last change on this file since b2ed712 was b2ed712, checked in by Sebastian Huber <sebastian.huber@…>, on 08/25/17 at 08:58:58

Include missing <string.h>

Update #2133.

  • Property mode set to 100644
File size: 15.3 KB
Line 
1/*  Memory Scrubber register driver
2 *
3 *  COPYRIGHT (c) 2017.
4 *  Cobham Gaisler AB.
5 *
6 *  The license and distribution terms for this file may be
7 *  found in the file LICENSE in this distribution or at
8 *  http://www.rtems.org/license/LICENSE.
9 */
10
11#include <stdint.h>
12#include <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15#include <rtems/bspIo.h>
16#include <drvmgr/drvmgr.h>
17#include <drvmgr/ambapp_bus.h>
18
19#include <bsp/memscrub.h>
20
21/*#define STATIC*/
22#define STATIC static
23
24#define UNUSED __attribute__((unused))
25
26/*#define DEBUG 1*/
27
28#ifdef DEBUG
29#define DBG(x...) printf(x)
30#else
31#define DBG(x...)
32#endif
33
34#define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
35#define REG_READ(addr) (*(volatile uint32_t *)(addr))
36
37/*
38 * MEMORYSCRUBBER AHBS register fields
39 * DEFINED IN HEADER FILE
40 */
41
42/*
43 * MEMORYSCRUBBER AHBERC register fields
44 * DEFINED IN HEADER FILE
45 */
46
47/*
48 * MEMORYSCRUBBER STAT register fields
49 * DEFINED IN HEADER FILE
50 */
51
52/*
53 * MEMORYSCRUBBER CONFIG register fields
54 * DEFINED IN HEADER FILE
55 */
56
57/*
58 * MEMORYSCRUBBER ETHRES register fields
59 * DEFINED IN HEADER FILE
60 */
61
62/* MEMORYSCRUBBER Registers layout */
63struct memscrub_regs {
64        volatile uint32_t ahbstatus; /* 0x00 */
65        volatile uint32_t ahbfailing; /* 0x04 */
66        volatile uint32_t ahberc; /* 0x08 */
67        volatile uint32_t resv1; /* 0x0c */
68        volatile uint32_t status; /* 0x10 */
69        volatile uint32_t config; /* 0x14 */
70        volatile uint32_t rangel; /* 0x18 */
71        volatile uint32_t rangeh; /* 0x1c */
72        volatile uint32_t pos; /* 0x20 */
73        volatile uint32_t ethres; /* 0x24 */
74        volatile uint32_t init; /* 0x28 */
75        volatile uint32_t rangel2; /* 0x2c */
76        volatile uint32_t rangeh2; /* 0x30 */
77};
78
79#define DEVNAME_LEN 10
80struct memscrub_priv {
81        struct drvmgr_dev *dev;
82        char devname[DEVNAME_LEN];
83        struct memscrub_regs *regs;
84        int minor;
85        int burstlen;
86        int blockmask;
87        /* Cached error */
88        uint32_t last_status;
89        uint32_t last_address;
90        /* User defined ISR */
91        memscrub_isr_t isr;
92        void *isr_arg;
93};
94static struct memscrub_priv * memscrubpriv = NULL;
95
96STATIC int memscrub_init2(struct drvmgr_dev *dev);
97STATIC int memscrub_init(struct memscrub_priv *priv);
98
99void memscrub_isr(void *arg);
100
101struct drvmgr_drv_ops memscrub_ops =
102{
103        .init = {NULL, memscrub_init2, NULL, NULL},
104        .remove = NULL,
105        .info = NULL
106};
107
108struct amba_dev_id memscrub_ids[] =
109{
110        {VENDOR_GAISLER, GAISLER_MEMSCRUB},
111        {0, 0}          /* Mark end of table */
112};
113
114struct amba_drv_info memscrub_drv_info =
115{
116        {
117                DRVMGR_OBJ_DRV,                 /* Driver */
118                NULL,                           /* Next driver */
119                NULL,                           /* Device list */
120                DRIVER_AMBAPP_GAISLER_MEMSCRUB_ID,/* Driver ID */
121                "MEMSCRUB_DRV",                 /* Driver Name */
122                DRVMGR_BUS_TYPE_AMBAPP,         /* Bus Type */
123                &memscrub_ops,
124                NULL,                           /* Funcs */
125                0,                              /* No devices yet */
126                sizeof(struct memscrub_priv),
127        },
128        &memscrub_ids[0]
129};
130
131void memscrub_register_drv (void)
132{
133        drvmgr_drv_register(&memscrub_drv_info.general);
134}
135
136STATIC int memscrub_init(struct memscrub_priv *priv)
137{
138        struct ambapp_ahb_info *ahb;
139        struct amba_dev_info *ainfo = priv->dev->businfo;
140        unsigned int tmp;
141        int i,j;
142
143        /* Get device information from AMBA PnP information */
144        if (ainfo == NULL){
145                return MEMSCRUB_ERR_ERROR;
146        }
147
148        /* Find MEMSCRUB core from Plug&Play information */
149        ahb = ainfo->info.ahb_slv;
150        priv->regs = (struct memscrub_regs *)ahb->start[0];
151
152        DBG("MEMSCRUB regs 0x%08x\n", (unsigned int) priv->regs);
153
154        /* Find MEMSCRUB capabilities */
155        tmp = REG_READ(&priv->regs->status);
156        i = (tmp & STAT_BURSTLEN) >> STAT_BURSTLEN_BIT;
157        for (j=1; i>0; i--) j <<= 1;
158        priv->burstlen = j;
159
160
161        /* If scrubber is active, we cannot stop it to read blockmask value */
162        if (tmp & STAT_ACTIVE){
163                priv->blockmask = 0;
164        }else{
165                /* Detect block size in bytes and burst length */
166                tmp = REG_READ(&priv->regs->rangeh);
167                REG_WRITE(&priv->regs->rangeh, 0);
168                priv->blockmask = REG_READ(&priv->regs->rangeh);
169                REG_WRITE(&priv->regs->rangeh, tmp);
170        }
171
172        /* DEBUG print */
173        DBG("MEMSCRUB with following capabilities:\n");
174        DBG(" -Burstlength: %d\n", priv->burstlen);
175
176        return MEMSCRUB_ERR_OK;
177}
178
179STATIC int memscrub_init2(struct drvmgr_dev *dev)
180{
181        struct memscrub_priv *priv = dev->priv;
182
183        DBG("MEMSCRUB[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
184
185        if (memscrubpriv) {
186                DBG("Driver only supports one MEMSCRUB core\n");
187                return DRVMGR_FAIL;
188        }
189
190        if (priv==NULL){
191                return DRVMGR_NOMEM;
192        }
193
194        /* Assign priv */
195        priv->dev = dev;
196        strncpy(&priv->devname[0], "memscrub0", DEVNAME_LEN);
197        memscrubpriv=priv;
198
199        /* Initilize driver struct */
200        if (memscrub_init(priv) != MEMSCRUB_ERR_OK){
201                return DRVMGR_FAIL;
202        }
203
204        /* Startup Action:
205         *      - Clear status
206         *      - Register ISR
207         */
208
209        /* Initialize hardware by clearing its status */
210        REG_WRITE(&priv->regs->ahbstatus, 0);
211        REG_WRITE(&priv->regs->status, 0);
212
213        return DRVMGR_OK;
214}
215
216
217int memscrub_init_start(uint32_t value, uint8_t delay, int options)
218{
219        struct memscrub_priv *priv = memscrubpriv;
220        uint32_t sts, tmp;
221        int i;
222
223        if (priv==NULL){
224                DBG("MEMSCRUB not init.\n");
225                return MEMSCRUB_ERR_ERROR;
226        }
227
228        /* Check if scrubber is active */
229        sts = REG_READ(&priv->regs->status);
230        if (sts & STAT_ACTIVE){
231                DBG("MEMSCRUB running.\n");
232                return MEMSCRUB_ERR_ERROR;
233        }
234
235        /* Check if we need to probe blockmask */
236        if (priv->blockmask == 0){
237                /* Detect block size in bytes and burst length */
238                tmp = REG_READ(&priv->regs->rangeh);
239                REG_WRITE(&priv->regs->rangeh, 0);
240                priv->blockmask = REG_READ(&priv->regs->rangeh);
241                REG_WRITE(&priv->regs->rangeh, tmp);
242        }
243
244        /* Set data value */
245        for (i=0; i<priv->blockmask; i+=4){
246                REG_WRITE(&priv->regs->init,value);
247        }
248
249        /* Clear unused bits */
250        options = options & ~(CONFIG_MODE | CONFIG_DELAY);
251
252        /* Enable scrubber */
253        REG_WRITE(&priv->regs->config, options |
254                        ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) |
255                        CONFIG_MODE_INIT | CONFIG_SCEN);
256
257        DBG("MEMSCRUB INIT STARTED\n");
258
259        return MEMSCRUB_ERR_OK;
260}
261
262int memscrub_scrub_start(uint8_t delay, int options)
263{
264        struct memscrub_priv *priv = memscrubpriv;
265        uint32_t ctrl,sts;
266
267        if (priv==NULL){
268                DBG("MEMSCRUB not init.\n");
269                return MEMSCRUB_ERR_ERROR;
270        }
271
272        /* Check if scrubber is active */
273        sts = REG_READ(&priv->regs->status);
274        if (sts & STAT_ACTIVE){
275                /* Check if mode is not init */
276                ctrl = REG_READ(&priv->regs->config);
277                if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
278                        DBG("MEMSCRUB init running.\n");
279                        return MEMSCRUB_ERR_ERROR;
280                }
281        }
282
283        /* Clear unused bits */
284        options = options & ~(CONFIG_MODE | CONFIG_DELAY);
285
286        /* Enable scrubber */
287        REG_WRITE(&priv->regs->config, options |
288                        ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) |
289                        CONFIG_MODE_SCRUB | CONFIG_SCEN);
290
291        DBG("MEMSCRUB SCRUB STARTED\n");
292
293        return MEMSCRUB_ERR_OK;
294}
295
296int memscrub_regen_start(uint8_t delay, int options)
297{
298        struct memscrub_priv *priv = memscrubpriv;
299        uint32_t ctrl,sts;
300
301        if (priv==NULL){
302                DBG("MEMSCRUB not init.\n");
303                return MEMSCRUB_ERR_ERROR;
304        }
305
306        /* Check if scrubber is active */
307        sts = REG_READ(&priv->regs->status);
308        if (sts & STAT_ACTIVE){
309                /* Check if mode is not init */
310                ctrl = REG_READ(&priv->regs->config);
311                if ((ctrl & CONFIG_MODE)==CONFIG_MODE_INIT){
312                        DBG("MEMSCRUB init running.\n");
313                        return MEMSCRUB_ERR_ERROR;
314                }
315        }
316
317        /* Clear unused bits */
318        options = options & ~(CONFIG_MODE | CONFIG_DELAY);
319
320        /* Enable scrubber */
321        REG_WRITE(&priv->regs->config, options |
322                        ((delay << CONFIG_DELAY_BIT) & CONFIG_DELAY) |
323                        CONFIG_MODE_REGEN | CONFIG_SCEN);
324
325        DBG("MEMSCRUB REGEN STARTED\n");
326
327        return MEMSCRUB_ERR_OK;
328}
329
330int memscrub_stop(void)
331{
332        struct memscrub_priv *priv = memscrubpriv;
333
334        if (priv==NULL){
335                DBG("MEMSCRUB not init.\n");
336                return MEMSCRUB_ERR_ERROR;
337        }
338
339        /* Disable scrubber */
340        REG_WRITE(&priv->regs->config, 0);
341
342        /* Wait until finished */
343        while(REG_READ(&priv->regs->status) & STAT_ACTIVE){};
344
345        DBG("MEMSCRUB STOPPED\n");
346
347        return MEMSCRUB_ERR_OK;
348}
349
350int memscrub_range_set(uint32_t start, uint32_t end)
351{
352        struct memscrub_priv *priv = memscrubpriv;
353
354        if (priv==NULL){
355                DBG("MEMSCRUB not init.\n");
356                return MEMSCRUB_ERR_ERROR;
357        }
358
359        if (end <= start){
360                DBG("MEMSCRUB wrong address.\n");
361                return MEMSCRUB_ERR_EINVAL;
362        }
363
364        /* Check if scrubber is active */
365        if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
366                DBG("MEMSCRUB running.\n");
367                return MEMSCRUB_ERR_ERROR;
368        }
369
370        /* Set range */
371        REG_WRITE(&priv->regs->rangel, start);
372        REG_WRITE(&priv->regs->rangeh, end);
373
374        DBG("MEMSCRUB range: 0x%08x-0x%08x\n",
375                        (unsigned int) start,
376                        (unsigned int) end);
377
378        return MEMSCRUB_ERR_OK;
379}
380
381int memscrub_secondary_range_set(uint32_t start, uint32_t end)
382{
383        struct memscrub_priv *priv = memscrubpriv;
384
385        if (priv==NULL){
386                DBG("MEMSCRUB not init.\n");
387                return MEMSCRUB_ERR_ERROR;
388        }
389
390        if (end <= start){
391                DBG("MEMSCRUB wrong address.\n");
392                return MEMSCRUB_ERR_EINVAL;
393        }
394
395        /* Check if scrubber is active */
396        if (REG_READ(&priv->regs->status) & STAT_ACTIVE){
397                DBG("MEMSCRUB running.\n");
398                return MEMSCRUB_ERR_ERROR;
399        }
400
401        /* Set range */
402        REG_WRITE(&priv->regs->rangel2, start);
403        REG_WRITE(&priv->regs->rangeh2, end);
404
405        DBG("MEMSCRUB 2nd range: 0x%08x-0x%08x\n",
406                        (unsigned int) start,
407                        (unsigned int) end);
408
409        return MEMSCRUB_ERR_OK;
410}
411
412int memscrub_range_get(uint32_t * start, uint32_t * end)
413{
414        struct memscrub_priv *priv = memscrubpriv;
415
416        if (priv==NULL){
417                DBG("MEMSCRUB not init.\n");
418                return MEMSCRUB_ERR_ERROR;
419        }
420
421        if ((start==NULL) || (end == NULL)){
422                DBG("MEMSCRUB wrong pointer.\n");
423                return MEMSCRUB_ERR_EINVAL;
424        }
425
426        /* Get range */
427        *start = REG_READ(&priv->regs->rangel);
428        *end = REG_READ(&priv->regs->rangeh);
429
430        return MEMSCRUB_ERR_OK;
431}
432
433int memscrub_secondary_range_get(uint32_t * start, uint32_t * end)
434{
435        struct memscrub_priv *priv = memscrubpriv;
436
437        if (priv==NULL){
438                DBG("MEMSCRUB not init.\n");
439                return MEMSCRUB_ERR_ERROR;
440        }
441
442        if ((start==NULL) || (end == NULL)){
443                DBG("MEMSCRUB wrong pointer.\n");
444                return MEMSCRUB_ERR_EINVAL;
445        }
446
447        /* Get range */
448        *start = REG_READ(&priv->regs->rangel2);
449        *end = REG_READ(&priv->regs->rangeh2);
450
451        return MEMSCRUB_ERR_OK;
452}
453
454int memscrub_ahberror_setup(int uethres, int cethres, int options)
455{
456        struct memscrub_priv *priv = memscrubpriv;
457
458        if (priv==NULL){
459                DBG("MEMSCRUB not init.\n");
460                return MEMSCRUB_ERR_ERROR;
461        }
462
463        /* Set AHBERR */
464        REG_WRITE(&priv->regs->ahberc,
465                        ((cethres << AHBERC_CECNTT_BIT) & AHBERC_CECNTT) |
466                        ((uethres << AHBERC_UECNTT_BIT) & AHBERC_UECNTT) |
467                        (options & (AHBERC_CECTE | AHBERC_UECTE)));
468
469        DBG("MEMSCRUB ahb err: UE[%d]:%s, CE[%d]:%s\n",
470                        (unsigned int) uethres,
471                        (options & AHBERC_UECTE)? "enabled":"disabled",
472                        (unsigned int) cethres,
473                        (options & AHBERC_CECTE)? "enabled":"disabled"
474                        );
475
476        return MEMSCRUB_ERR_OK;
477}
478
479int memscrub_scruberror_setup(int blkthres, int runthres, int options)
480{
481        struct memscrub_priv *priv = memscrubpriv;
482
483        if (priv==NULL){
484                DBG("MEMSCRUB not init.\n");
485                return MEMSCRUB_ERR_ERROR;
486        }
487
488        /* Set ETHRES */
489        REG_WRITE(&priv->regs->ethres,
490                        ((blkthres << ETHRES_BECT_BIT) & ETHRES_BECT) |
491                        ((runthres << ETHRES_RECT_BIT) & ETHRES_RECT) |
492                        (options & (ETHRES_RECTE | ETHRES_BECTE)));
493
494        DBG("MEMSCRUB scrub err: BLK[%d]:%s, RUN[%d]:%s\n",
495                        (unsigned int) blkthres,
496                        (options & ETHRES_BECTE)? "enabled":"disabled",
497                        (unsigned int) runthres,
498                        (options & ETHRES_RECTE)? "enabled":"disabled"
499                        );
500
501        return MEMSCRUB_ERR_OK;
502}
503
504int memscrub_scrub_position(uint32_t * position)
505{
506        struct memscrub_priv *priv = memscrubpriv;
507
508        if (priv==NULL){
509                DBG("MEMSCRUB not init.\n");
510                return MEMSCRUB_ERR_ERROR;
511        }
512
513        if (position==NULL){
514                DBG("MEMSCRUB wrong pointer.\n");
515                return MEMSCRUB_ERR_EINVAL;
516        }
517
518        *position = REG_READ(&priv->regs->pos);
519
520        return MEMSCRUB_ERR_OK;
521}
522
523int memscrub_isr_register(memscrub_isr_t isr, void * data)
524{
525        struct memscrub_priv *priv = memscrubpriv;
526        unsigned int ethres, ahberc, config;
527
528        if (priv==NULL){
529                DBG("MEMSCRUB not init.\n");
530                return MEMSCRUB_ERR_ERROR;
531        }
532
533        if (isr==NULL){
534                DBG("MEMSCRUB wrong pointer.\n");
535                return MEMSCRUB_ERR_EINVAL;
536        }
537
538        /* Mask interrupts */
539        ethres = REG_READ(&priv->regs->ethres);
540        REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
541
542        ahberc = REG_READ(&priv->regs->ahberc);
543        REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
544
545        config = REG_READ(&priv->regs->config);
546        REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
547
548        /* Install IRQ handler if needed */
549        if (priv->isr == NULL){
550                drvmgr_interrupt_register(priv->dev, 0, priv->devname, memscrub_isr,
551                                priv);
552        }
553
554        /* Install user ISR */
555        priv->isr=isr;
556        priv->isr_arg=data;
557
558        /* Unmask interrupts */
559        REG_WRITE(&priv->regs->ethres, ethres);
560
561        REG_WRITE(&priv->regs->ahberc, ahberc);
562
563        REG_WRITE(&priv->regs->config, config);
564
565        return MEMSCRUB_ERR_OK;
566}
567
568int memscrub_isr_unregister(void)
569{
570        struct memscrub_priv *priv = memscrubpriv;
571        unsigned int ethres, ahberc, config;
572
573        if (priv==NULL){
574                DBG("MEMSCRUB not init.\n");
575                return MEMSCRUB_ERR_ERROR;
576        }
577
578        if (priv->isr==NULL){
579                DBG("MEMSCRUB wrong pointer.\n");
580                return MEMSCRUB_ERR_EINVAL;
581        }
582
583        /* Mask interrupts */
584        ethres = REG_READ(&priv->regs->ethres);
585        REG_WRITE(&priv->regs->ethres, ethres & ~(ETHRES_RECTE | ETHRES_BECTE));
586
587        ahberc = REG_READ(&priv->regs->ahberc);
588        REG_WRITE(&priv->regs->ahberc, ahberc & ~(AHBERC_CECTE | AHBERC_UECTE));
589
590        config = REG_READ(&priv->regs->config);
591        REG_WRITE(&priv->regs->config, config & ~(CONFIG_IRQD));
592
593        /* Uninstall IRQ handler if needed */
594        drvmgr_interrupt_unregister(priv->dev, 0, memscrub_isr, priv);
595
596        /* Uninstall user ISR */
597        priv->isr=NULL;
598        priv->isr_arg=NULL;
599
600        return MEMSCRUB_ERR_OK;
601}
602
603int memscrub_error_status(uint32_t *ahbaccess, uint32_t *ahbstatus,
604                uint32_t *scrubstatus)
605{
606        struct memscrub_priv *priv = memscrubpriv;
607        uint32_t mask, ahbstatus_val;
608
609        if (priv==NULL){
610                DBG("MEMSCRUB not init.\n");
611                return MEMSCRUB_ERR_ERROR;
612        }
613
614        if ((ahbaccess==NULL) || (ahbstatus==NULL) || (scrubstatus == NULL)){
615                DBG("MEMSCRUB wrong pointer.\n");
616                return MEMSCRUB_ERR_EINVAL;
617        }
618
619        /* Get hardware status */
620        *ahbaccess = REG_READ(&priv->regs->ahbfailing);
621        *ahbstatus = ahbstatus_val = REG_READ(&priv->regs->ahbstatus);
622        *scrubstatus = REG_READ(&priv->regs->status);
623
624        /* Clear error status */
625        mask = 0;
626        /* Clear CECNT only if we crossed the CE threshold*/
627        if ((ahbstatus_val & AHBS_CE) == 0){
628                /* Don't clear the CECNT */
629                mask |= AHBS_CECNT;
630        }
631        /* Clear UECNT only if we crossed the UE threshold*/
632        if ((ahbstatus_val & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
633                /* Don't clear the UECNT */
634                mask |= AHBS_UECNT;
635        }
636        REG_WRITE(&priv->regs->ahbstatus, ahbstatus_val & mask);
637        REG_WRITE(&priv->regs->status,0);
638
639        return MEMSCRUB_ERR_OK;
640}
641
642int memscrub_active(void)
643{
644        struct memscrub_priv *priv = memscrubpriv;
645
646        if (priv==NULL){
647                DBG("MEMSCRUB not init.\n");
648                return MEMSCRUB_ERR_ERROR;
649        }
650
651        return REG_READ(&priv->regs->status) & STAT_ACTIVE;
652}
653
654void memscrub_isr(void *arg)
655{
656        struct memscrub_priv *priv = arg;
657        uint32_t fadr, ahbstatus, status, mask;
658
659        /* Get hardware status */
660        ahbstatus = REG_READ(&priv->regs->ahbstatus);
661        if ((ahbstatus & (AHBS_NE|AHBS_DONE)) == 0){
662                return;
663        }
664
665        /* IRQ generated by MEMSCRUB core... handle it here */
666
667        /* Get Failing address */
668        fadr = REG_READ(&priv->regs->ahbfailing);
669
670        /* Get Status */
671        status = REG_READ(&priv->regs->status);
672
673        /* Clear error status */
674        mask = 0;
675        /* Clear CECNT only if we crossed the CE threshold*/
676        if ((ahbstatus & AHBS_CE) == 0){
677                /* Don't clear the CECNT */
678                mask |= AHBS_CECNT;
679        }
680        /* Clear UECNT only if we crossed the UE threshold*/
681        if ((ahbstatus & (AHBS_NE|AHBS_CE|AHBS_SBC|AHBS_SEC)) != AHBS_NE){
682                /* Don't clear the UECNT */
683                mask |= AHBS_UECNT;
684        }
685        REG_WRITE(&priv->regs->ahbstatus, ahbstatus & mask);
686        REG_WRITE(&priv->regs->status,0);
687
688        /* Let user handle error */
689        (priv->isr)(priv->isr_arg, fadr, ahbstatus, status);
690
691        return;
692}
Note: See TracBrowser for help on using the repository browser.