summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/amba/ahbstat.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-22 18:31:04 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-01-22 12:46:33 +0100
commit7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch)
tree085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/amba/ahbstat.c
parentgrlib: Move header files (diff)
downloadrtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/amba/ahbstat.c')
-rw-r--r--bsps/shared/grlib/amba/ahbstat.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/bsps/shared/grlib/amba/ahbstat.c b/bsps/shared/grlib/amba/ahbstat.c
new file mode 100644
index 0000000000..af3d778feb
--- /dev/null
+++ b/bsps/shared/grlib/amba/ahbstat.c
@@ -0,0 +1,239 @@
+/* AHB Status register driver
+ *
+ * COPYRIGHT (c) 2009 - 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 <inttypes.h>
+#include <string.h>
+#include <rtems.h>
+#include <rtems/bspIo.h>
+#include <drvmgr/drvmgr.h>
+#include <grlib/ambapp_bus.h>
+
+#include <grlib/ahbstat.h>
+
+#include <grlib/grlib_impl.h>
+
+#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);
+
+/* AHB fail interrupt callback to user. This function is declared weak so that
+ * the user can define a function pointer variable containing the address
+ * responsible for handling errors
+ *
+ * minor Index of AHBSTAT hardware
+ * regs Register address of AHBSTAT
+ * status AHBSTAT status register at IRQ
+ * failing_address AHBSTAT Failing address register at IRQ
+ *
+ * * User return
+ * 0: print error onto terminal with printk and reenable AHBSTAT
+ * 1: just re-enable AHBSTAT
+ * 2: just print error
+ * 3: do nothing, let user do custom handling
+ */
+int (*ahbstat_error)(
+ int minor,
+ struct ahbstat_regs *regs,
+ uint32_t status,
+ uint32_t failing_address
+ ) __attribute__((weak)) = NULL;
+
+#define AHBSTAT_STS_CE_BIT 9
+#define AHBSTAT_STS_NE_BIT 8
+#define AHBSTAT_STS_HW_BIT 7
+#define AHBSTAT_STS_HM_BIT 3
+#define AHBSTAT_STS_HS_BIT 0
+
+#define AHBSTAT_STS_CE (1 << AHBSTAT_STS_CE_BIT)
+#define AHBSTAT_STS_NE (1 << AHBSTAT_STS_NE_BIT)
+#define AHBSTAT_STS_HW (1 << AHBSTAT_STS_HW_BIT)
+#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_DECLARE(devlock);
+};
+
+static int ahbstat_init2(struct drvmgr_dev *dev);
+
+struct drvmgr_drv_ops ahbstat_ops =
+{
+ .init = {NULL, ahbstat_init2, NULL, NULL},
+ .remove = NULL,
+ .info = NULL
+};
+
+struct amba_dev_id ahbstat_ids[] =
+{
+ {VENDOR_GAISLER, GAISLER_AHBSTAT},
+ {0, 0} /* Mark end of table */
+};
+
+struct amba_drv_info ahbstat_drv_info =
+{
+ {
+ DRVMGR_OBJ_DRV, /* Driver */
+ NULL, /* Next driver */
+ NULL, /* Device list */
+ DRIVER_AMBAPP_GAISLER_AHBSTAT_ID,/* Driver ID */
+ "AHBSTAT_DRV", /* Driver Name */
+ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
+ &ahbstat_ops,
+ NULL, /* Funcs */
+ 0, /* No devices yet */
+ sizeof(struct ahbstat_priv),
+ },
+ &ahbstat_ids[0]
+};
+
+void ahbstat_register_drv (void)
+{
+ drvmgr_drv_register(&ahbstat_drv_info.general);
+}
+
+static int ahbstat_init2(struct drvmgr_dev *dev)
+{
+ struct ahbstat_priv *priv;
+ struct amba_dev_info *ambadev;
+
+ priv = dev->priv;
+ if (!priv)
+ return DRVMGR_NOMEM;
+ priv->dev = dev;
+
+ /* Get device information from AMBA PnP information */
+ ambadev = (struct amba_dev_info *)dev->businfo;
+ if (ambadev == NULL)
+ return DRVMGR_FAIL;
+ 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_INIT(&priv->devlock, priv->devname);
+
+ /* Initialize hardware */
+ REG_WRITE(&priv->regs->status, 0);
+
+ /* Install IRQ handler */
+ drvmgr_interrupt_register(dev, 0, priv->devname, ahbstat_isr, priv);
+
+ return DRVMGR_OK;
+}
+
+void ahbstat_isr(void *arg)
+{
+ struct ahbstat_priv *priv = arg;
+ uint32_t fadr, status;
+ int rc;
+ SPIN_ISR_IRQFLAGS(lock_context);
+
+ /* Get hardware 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 = REG_READ(&priv->regs->failing);
+
+ SPIN_LOCK(&priv->devlock, lock_context);
+ priv->last_status = status;
+ priv->last_address = fadr;
+ SPIN_UNLOCK(&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
+ * 3: do nothing
+ */
+ rc = 0;
+ if (ahbstat_error != NULL)
+ rc = ahbstat_error(priv->minor, priv->regs, status, fadr);
+
+ if ((rc & 0x1) == 0) {
+ printk("\n### AHBSTAT: %s %s error of size %" PRId32
+ " by master %" PRId32 " at 0x%08" PRIx32 "\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,
+ fadr);
+ }
+
+ if ((rc & 0x2) == 0) {
+ /* Trigger new interrupts */
+ REG_WRITE(&priv->regs->status, 0);
+ }
+}
+
+/* Get Last received AHB Error
+ *
+ * Return
+ * 0: No error received
+ * 1: Error Received, last status and address stored into argument pointers
+ * -1: No such AHBSTAT device
+ */
+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_IRQFLAGS(lock_context);
+
+ if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
+ return -1;
+ }
+ priv = (struct ahbstat_priv *)dev->priv;
+
+ /* Read information cached by ISR */
+ SPIN_LOCK_IRQ(&priv->devlock, lock_context);
+ last_status = REG_READ(&priv->last_status);
+ last_address = REG_READ(&priv->last_address);
+ SPIN_UNLOCK_IRQ(&priv->devlock, lock_context);
+
+ *status = last_status;
+ *address = last_address;
+
+ return (last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT;
+}
+
+/* Get AHBSTAT registers address from minor. NULL returned if no such device */
+struct ahbstat_regs *ahbstat_get_regs(int minor)
+{
+ struct drvmgr_dev *dev;
+ struct ahbstat_priv *priv;
+
+ if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
+ return NULL;
+ }
+ priv = (struct ahbstat_priv *)dev->priv;
+
+ return priv->regs;
+}