summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Aberg <maberg@gaisler.com>2017-01-19 18:57:58 +0100
committerDaniel Hellstrom <daniel@gaisler.com>2017-05-02 12:34:48 +0200
commitc609cceabbf8598be400fc9bb054d230a5eefcd6 (patch)
treef63360954d78feb735cb9bdd20de02a281667884
parentleon, memscrub: add GR740 memory scrubber driver (diff)
downloadrtems-c609cceabbf8598be400fc9bb054d230a5eefcd6.tar.bz2
leon, ahbstat: Use RTEMS 4.12 SMP interrupt lock
-rw-r--r--c/src/lib/libbsp/sparc/shared/amba/ahbstat.c65
1 files changed, 51 insertions, 14 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c b/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c
index 8fff66c8f5..ee697f6647 100644
--- a/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c
+++ b/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c
@@ -1,6 +1,6 @@
/* AHB Status register driver
*
- * COPYRIGHT (c) 2009.
+ * COPYRIGHT (c) 2009 - 2017.
* Cobham Gaisler AB.
*
* The license and distribution terms for this file may be
@@ -9,11 +9,23 @@
*/
#include <stdint.h>
+#include <rtems.h>
+#include <rtems/bspIo.h>
#include <drvmgr/drvmgr.h>
#include <drvmgr/ambapp_bus.h>
#include <bsp/ahbstat.h>
-#include <rtems/bspIo.h>
+
+#define SPIN_IRQ_DECLARE(name) RTEMS_INTERRUPT_LOCK_DECLARE(, name)
+#define SPIN_IRQ_INIT(lock, name) rtems_interrupt_lock_initialize(lock, name)
+#define SPIN_IRQ_LOCK(lock, ctx) rtems_interrupt_lock_acquire(lock, &(ctx))
+#define SPIN_IRQ_UNLOCK(lock, ctx) rtems_interrupt_lock_release(lock, &(ctx))
+#define SPIN_IRQ_LOCK_ISR(lock, ctx) rtems_interrupt_lock_acquire_isr(lock, &(ctx))
+#define SPIN_IRQ_UNLOCK_ISR(lock, ctx) rtems_interrupt_lock_release_isr(lock, &(ctx))
+#define SPIN_IRQ_CTX rtems_interrupt_lock_context
+
+#define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
+#define REG_READ(addr) (*(volatile uint32_t *)(addr))
void ahbstat_isr(void *arg);
@@ -51,12 +63,17 @@ int (*ahbstat_error)(
#define AHBSTAT_STS_HM (0xf << AHBSTAT_STS_HM_BIT)
#define AHBSTAT_STS_HS (0x7 << AHBSTAT_STS_HS_BIT)
+enum { DEVNAME_LEN = 9 };
struct ahbstat_priv {
struct drvmgr_dev *dev;
struct ahbstat_regs *regs;
+ char devname[DEVNAME_LEN];
int minor;
+ /* Cached error */
uint32_t last_status;
uint32_t last_address;
+ /* Spin-lock ISR protection */
+ SPIN_IRQ_DECLARE(devlock);
};
static int ahbstat_init2(struct drvmgr_dev *dev);
@@ -113,11 +130,19 @@ static int ahbstat_init2(struct drvmgr_dev *dev)
priv->regs = (struct ahbstat_regs *)ambadev->info.apb_slv->start;
priv->minor = dev->minor_drv;
+ strncpy(&priv->devname[0], "ahbstat0", DEVNAME_LEN);
+ priv->devname[7] += priv->minor;
+ /*
+ * Initialize spinlock for AHBSTAT Device. It is used to protect user
+ * API calls involivng priv structure from updates in ISR.
+ */
+ SPIN_IRQ_INIT(&priv->devlock, priv->devname);
+
/* Initialize hardware */
- priv->regs->status = 0;
+ REG_WRITE(&priv->regs->status, 0);
/* Install IRQ handler */
- drvmgr_interrupt_register(dev, 0, "ahbstat", ahbstat_isr, priv);
+ drvmgr_interrupt_register(dev, 0, priv->devname, ahbstat_isr, priv);
return DRVMGR_OK;
}
@@ -127,26 +152,29 @@ void ahbstat_isr(void *arg)
struct ahbstat_priv *priv = arg;
uint32_t fadr, status;
int rc;
+ SPIN_IRQ_CTX lock_context;
/* Get hardware status */
- status = priv->regs->status;
+ status = REG_READ(&priv->regs->status);
if ((status & AHBSTAT_STS_NE) == 0)
return;
/* IRQ generated by AHBSTAT core... handle it here */
/* Get Failing address */
- fadr = priv->regs->failing;
+ fadr = REG_READ(&priv->regs->failing);
+ SPIN_IRQ_LOCK_ISR(&priv->devlock, lock_context);
priv->last_status = status;
priv->last_address = fadr;
+ SPIN_IRQ_UNLOCK_ISR(&priv->devlock, lock_context);
/* Let user handle error, default to print the error and reenable HW
*
* User return
* 0: print error and reenable AHBSTAT
* 1: just reenable AHBSTAT
- * 2: just print error reenable
+ * 2: just print error
* 3: do nothing
*/
rc = 0;
@@ -154,18 +182,18 @@ void ahbstat_isr(void *arg)
rc = ahbstat_error(priv->minor, priv->regs, status, fadr);
if ((rc & 0x1) == 0) {
- printk("\n### AHBSTAT: %s %s error of size %lu by master %ld"
+ printk("\n### AHBSTAT: %s %s error of size %ld by master %ld"
" at 0x%08lx\n",
status & AHBSTAT_STS_CE ? "single" : "non-correctable",
status & AHBSTAT_STS_HW ? "write" : "read",
- (status & AHBSTAT_STS_HS) >> AHBSTAT_STS_HS_BIT,
- (status & AHBSTAT_STS_HM) >> AHBSTAT_STS_HM_BIT,
+ (int) (status & AHBSTAT_STS_HS) >> AHBSTAT_STS_HS_BIT,
+ (int) (status & AHBSTAT_STS_HM) >> AHBSTAT_STS_HM_BIT,
fadr);
}
if ((rc & 0x2) == 0) {
/* Trigger new interrupts */
- priv->regs->status = 0;
+ REG_WRITE(&priv->regs->status, 0);
}
}
@@ -180,16 +208,25 @@ int ahbstat_last_error(int minor, uint32_t *status, uint32_t *address)
{
struct drvmgr_dev *dev;
struct ahbstat_priv *priv;
+ uint32_t last_status;
+ uint32_t last_address;
+ SPIN_IRQ_CTX lock_context;
if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
return -1;
}
priv = (struct ahbstat_priv *)dev->priv;
- *status = priv->last_status;
- *address = priv->last_address;
+ /* Read information cached by ISR */
+ SPIN_IRQ_LOCK(&priv->devlock, lock_context);
+ last_status = REG_READ(&priv->last_status);
+ last_address = REG_READ(&priv->last_address);
+ SPIN_IRQ_UNLOCK(&priv->devlock, lock_context);
+
+ *status = last_status;
+ *address = last_address;
- return (priv->last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT;
+ return (last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT;
}
/* Get AHBSTAT registers address from minor. NULL returned if no such device */