source: rtems/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c @ a8595605

5
Last change on this file since a8595605 was c609ccea, checked in by Martin Aberg <maberg@…>, on 01/19/17 at 17:57:58

leon, ahbstat: Use RTEMS 4.12 SMP interrupt lock

  • Property mode set to 100644
File size: 6.5 KB
Line 
1/*  AHB Status register driver
2 *
3 *  COPYRIGHT (c) 2009 - 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 <rtems.h>
13#include <rtems/bspIo.h>
14#include <drvmgr/drvmgr.h>
15#include <drvmgr/ambapp_bus.h>
16
17#include <bsp/ahbstat.h>
18
19#define SPIN_IRQ_DECLARE(name)          RTEMS_INTERRUPT_LOCK_DECLARE(, name)
20#define SPIN_IRQ_INIT(lock, name)       rtems_interrupt_lock_initialize(lock, name)
21#define SPIN_IRQ_LOCK(lock, ctx)        rtems_interrupt_lock_acquire(lock, &(ctx))
22#define SPIN_IRQ_UNLOCK(lock, ctx)      rtems_interrupt_lock_release(lock, &(ctx))
23#define SPIN_IRQ_LOCK_ISR(lock, ctx)    rtems_interrupt_lock_acquire_isr(lock, &(ctx))
24#define SPIN_IRQ_UNLOCK_ISR(lock, ctx)  rtems_interrupt_lock_release_isr(lock, &(ctx))
25#define SPIN_IRQ_CTX                    rtems_interrupt_lock_context
26
27#define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
28#define REG_READ(addr) (*(volatile uint32_t *)(addr))
29
30void ahbstat_isr(void *arg);
31
32/* AHB fail interrupt callback to user. This function is declared weak so that
33 * the user can define a function pointer variable containing the address
34 * responsible for handling errors
35 *
36 * minor              Index of AHBSTAT hardware
37 * regs               Register address of AHBSTAT
38 * status             AHBSTAT status register at IRQ
39 * failing_address    AHBSTAT Failing address register at IRQ
40 *
41 * * User return
42 *  0: print error onto terminal with printk and reenable AHBSTAT
43 *  1: just re-enable AHBSTAT
44 *  2: just print error
45 *  3: do nothing, let user do custom handling
46 */
47int (*ahbstat_error)(
48        int minor,
49        struct ahbstat_regs *regs,
50        uint32_t status,
51        uint32_t failing_address
52        ) __attribute__((weak)) = NULL;
53
54#define AHBSTAT_STS_CE_BIT 9
55#define AHBSTAT_STS_NE_BIT 8
56#define AHBSTAT_STS_HW_BIT 7
57#define AHBSTAT_STS_HM_BIT 3
58#define AHBSTAT_STS_HS_BIT 0
59
60#define AHBSTAT_STS_CE (1 << AHBSTAT_STS_CE_BIT)
61#define AHBSTAT_STS_NE (1 << AHBSTAT_STS_NE_BIT)
62#define AHBSTAT_STS_HW (1 << AHBSTAT_STS_HW_BIT)
63#define AHBSTAT_STS_HM (0xf << AHBSTAT_STS_HM_BIT)
64#define AHBSTAT_STS_HS (0x7 << AHBSTAT_STS_HS_BIT)
65
66enum { DEVNAME_LEN = 9 };
67struct ahbstat_priv {
68        struct drvmgr_dev *dev;
69        struct ahbstat_regs *regs;
70        char devname[DEVNAME_LEN];
71        int minor;
72        /* Cached error */
73        uint32_t last_status;
74        uint32_t last_address;
75        /* Spin-lock ISR protection */
76        SPIN_IRQ_DECLARE(devlock);
77};
78
79static int ahbstat_init2(struct drvmgr_dev *dev);
80
81struct drvmgr_drv_ops ahbstat_ops =
82{
83        .init = {NULL, ahbstat_init2, NULL, NULL},
84        .remove = NULL,
85        .info = NULL
86};
87
88struct amba_dev_id ahbstat_ids[] =
89{
90        {VENDOR_GAISLER, GAISLER_AHBSTAT},
91        {0, 0}          /* Mark end of table */
92};
93
94struct amba_drv_info ahbstat_drv_info =
95{
96        {
97                DRVMGR_OBJ_DRV,                 /* Driver */
98                NULL,                           /* Next driver */
99                NULL,                           /* Device list */
100                DRIVER_AMBAPP_GAISLER_AHBSTAT_ID,/* Driver ID */
101                "AHBSTAT_DRV",                  /* Driver Name */
102                DRVMGR_BUS_TYPE_AMBAPP,         /* Bus Type */
103                &ahbstat_ops,
104                NULL,                           /* Funcs */
105                0,                              /* No devices yet */
106                sizeof(struct ahbstat_priv),
107        },
108        &ahbstat_ids[0]
109};
110
111void ahbstat_register_drv (void)
112{
113        drvmgr_drv_register(&ahbstat_drv_info.general);
114}
115
116static int ahbstat_init2(struct drvmgr_dev *dev)
117{
118        struct ahbstat_priv *priv;
119        struct amba_dev_info *ambadev;
120
121        priv = dev->priv;
122        if (!priv)
123                return DRVMGR_NOMEM;
124        priv->dev = dev;
125
126        /* Get device information from AMBA PnP information */
127        ambadev = (struct amba_dev_info *)dev->businfo;
128        if (ambadev == NULL)
129                return DRVMGR_FAIL;
130        priv->regs = (struct ahbstat_regs *)ambadev->info.apb_slv->start;
131        priv->minor = dev->minor_drv;
132
133        strncpy(&priv->devname[0], "ahbstat0", DEVNAME_LEN);
134        priv->devname[7] += priv->minor;
135        /*
136         * Initialize spinlock for AHBSTAT Device. It is used to protect user
137         * API calls involivng priv structure from updates in ISR.
138         */
139        SPIN_IRQ_INIT(&priv->devlock, priv->devname);
140
141        /* Initialize hardware */
142        REG_WRITE(&priv->regs->status, 0);
143
144        /* Install IRQ handler */
145        drvmgr_interrupt_register(dev, 0, priv->devname, ahbstat_isr, priv);
146
147        return DRVMGR_OK;
148}
149
150void ahbstat_isr(void *arg)
151{
152        struct ahbstat_priv *priv = arg;
153        uint32_t fadr, status;
154        int rc;
155        SPIN_IRQ_CTX lock_context;
156
157        /* Get hardware status */
158        status = REG_READ(&priv->regs->status);
159        if ((status & AHBSTAT_STS_NE) == 0)
160                return;
161
162        /* IRQ generated by AHBSTAT core... handle it here */
163
164        /* Get Failing address */
165        fadr = REG_READ(&priv->regs->failing);
166
167        SPIN_IRQ_LOCK_ISR(&priv->devlock, lock_context);
168        priv->last_status = status;
169        priv->last_address = fadr;
170        SPIN_IRQ_UNLOCK_ISR(&priv->devlock, lock_context);
171
172        /* Let user handle error, default to print the error and reenable HW
173         *
174         * User return
175         *  0: print error and reenable AHBSTAT
176         *  1: just reenable AHBSTAT
177         *  2: just print error
178         *  3: do nothing
179         */
180        rc = 0;
181        if (ahbstat_error != NULL)
182                rc = ahbstat_error(priv->minor, priv->regs, status, fadr);
183
184        if ((rc & 0x1) == 0) {
185                printk("\n### AHBSTAT: %s %s error of size %ld by master %ld"
186                        " at 0x%08lx\n",
187                        status & AHBSTAT_STS_CE ? "single" : "non-correctable",
188                        status & AHBSTAT_STS_HW ? "write" : "read",
189                        (int) (status & AHBSTAT_STS_HS) >> AHBSTAT_STS_HS_BIT,
190                        (int) (status & AHBSTAT_STS_HM) >> AHBSTAT_STS_HM_BIT,
191                        fadr);
192        }
193
194        if ((rc & 0x2) == 0) {
195                /* Trigger new interrupts */
196                REG_WRITE(&priv->regs->status, 0);
197        }
198}
199
200/* Get Last received AHB Error
201 *
202 * Return
203 *   0: No error received
204 *   1: Error Received, last status and address stored into argument pointers
205 *  -1: No such AHBSTAT device
206 */
207int ahbstat_last_error(int minor, uint32_t *status, uint32_t *address)
208{
209        struct drvmgr_dev *dev;
210        struct ahbstat_priv *priv;
211        uint32_t last_status;
212        uint32_t last_address;
213        SPIN_IRQ_CTX lock_context;
214
215        if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
216                return -1;
217        }
218        priv = (struct ahbstat_priv *)dev->priv;
219
220        /* Read information cached by ISR */
221        SPIN_IRQ_LOCK(&priv->devlock, lock_context);
222        last_status = REG_READ(&priv->last_status);
223        last_address = REG_READ(&priv->last_address);
224        SPIN_IRQ_UNLOCK(&priv->devlock, lock_context);
225
226        *status = last_status;
227        *address = last_address;
228
229        return (last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT;
230}
231
232/* Get AHBSTAT registers address from minor. NULL returned if no such device */
233struct ahbstat_regs *ahbstat_get_regs(int minor)
234{
235        struct drvmgr_dev *dev;
236        struct ahbstat_priv *priv;
237
238        if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
239                return NULL;
240        }
241        priv = (struct ahbstat_priv *)dev->priv;
242
243        return priv->regs;
244}
Note: See TracBrowser for help on using the repository browser.