diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-22 18:31:04 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2019-01-22 12:46:33 +0100 |
commit | 7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch) | |
tree | 085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/amba/ahbstat.c | |
parent | grlib: Move header files (diff) | |
download | rtems-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.c | 239 |
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; +} |