Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*  AHB Status register driver
0004  *
0005  *  COPYRIGHT (c) 2009 - 2017.
0006  *  Cobham Gaisler AB.
0007  *
0008  * Redistribution and use in source and binary forms, with or without
0009  * modification, are permitted provided that the following conditions
0010  * are met:
0011  * 1. Redistributions of source code must retain the above copyright
0012  *    notice, this list of conditions and the following disclaimer.
0013  * 2. Redistributions in binary form must reproduce the above copyright
0014  *    notice, this list of conditions and the following disclaimer in the
0015  *    documentation and/or other materials provided with the distribution.
0016  *
0017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0027  * POSSIBILITY OF SUCH DAMAGE.
0028  */
0029 
0030 #include <inttypes.h>
0031 #include <string.h>
0032 #include <rtems.h>
0033 #include <rtems/bspIo.h>
0034 #include <drvmgr/drvmgr.h>
0035 #include <grlib/ambapp_bus.h>
0036 
0037 #include <grlib/ahbstat.h>
0038 
0039 #include <grlib/grlib_impl.h>
0040 
0041 #define REG_WRITE(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
0042 #define REG_READ(addr) (*(volatile uint32_t *)(addr))
0043 
0044 void ahbstat_isr(void *arg);
0045 
0046 /* AHB fail interrupt callback to user. This function is declared weak so that
0047  * the user can define a function pointer variable containing the address
0048  * responsible for handling errors
0049  *
0050  * minor              Index of AHBSTAT hardware
0051  * regs               Register address of AHBSTAT
0052  * status             AHBSTAT status register at IRQ
0053  * failing_address    AHBSTAT Failing address register at IRQ
0054  *
0055  * * User return 
0056  *  0: print error onto terminal with printk and reenable AHBSTAT
0057  *  1: just re-enable AHBSTAT
0058  *  2: just print error
0059  *  3: do nothing, let user do custom handling
0060  */
0061 int (*ahbstat_error)(
0062     int minor,
0063     struct ahbstat_regs *regs,
0064     uint32_t status,
0065     uint32_t failing_address
0066     ) __attribute__((weak)) = NULL;
0067 
0068 #define AHBSTAT_STS_ME_BIT 13
0069 #define AHBSTAT_STS_FW_BIT 12
0070 #define AHBSTAT_STS_CF_BIT 11
0071 #define AHBSTAT_STS_AF_BIT 10
0072 #define AHBSTAT_STS_CE_BIT 9
0073 #define AHBSTAT_STS_NE_BIT 8
0074 #define AHBSTAT_STS_HW_BIT 7
0075 #define AHBSTAT_STS_HM_BIT 3
0076 #define AHBSTAT_STS_HS_BIT 0
0077 
0078 #define AHBSTAT_STS_ME (1 << AHBSTAT_STS_ME_BIT)
0079 #define AHBSTAT_STS_FW (1 << AHBSTAT_STS_FW_BIT)
0080 #define AHBSTAT_STS_CF (1 << AHBSTAT_STS_CF_BIT)
0081 #define AHBSTAT_STS_AF (1 << AHBSTAT_STS_AF_BIT)
0082 #define AHBSTAT_STS_CE (1 << AHBSTAT_STS_CE_BIT)
0083 #define AHBSTAT_STS_NE (1 << AHBSTAT_STS_NE_BIT)
0084 #define AHBSTAT_STS_HW (1 << AHBSTAT_STS_HW_BIT)
0085 #define AHBSTAT_STS_HM (0xf << AHBSTAT_STS_HM_BIT)
0086 #define AHBSTAT_STS_HS (0x7 << AHBSTAT_STS_HS_BIT)
0087 
0088 enum { DEVNAME_LEN = 9 };
0089 struct ahbstat_priv {
0090     struct drvmgr_dev *dev;
0091     struct ahbstat_regs *regs;
0092     char devname[DEVNAME_LEN];
0093     int minor;
0094     /* Cached error */
0095     uint32_t last_status;
0096     uint32_t last_address;
0097     /* Spin-lock ISR protection */
0098     SPIN_DECLARE(devlock);
0099 };
0100 
0101 static int ahbstat_init2(struct drvmgr_dev *dev);
0102 
0103 struct drvmgr_drv_ops ahbstat_ops =
0104 {
0105     .init = {NULL, ahbstat_init2, NULL, NULL},
0106     .remove = NULL,
0107     .info = NULL
0108 };
0109 
0110 struct amba_dev_id ahbstat_ids[] =
0111 {
0112     {VENDOR_GAISLER, GAISLER_AHBSTAT},
0113     {0, 0}      /* Mark end of table */
0114 };
0115 
0116 struct amba_drv_info ahbstat_drv_info =
0117 {
0118     {
0119         DRVMGR_OBJ_DRV,         /* Driver */
0120         NULL,               /* Next driver */
0121         NULL,               /* Device list */
0122         DRIVER_AMBAPP_GAISLER_AHBSTAT_ID,/* Driver ID */
0123         "AHBSTAT_DRV",          /* Driver Name */
0124         DRVMGR_BUS_TYPE_AMBAPP,     /* Bus Type */
0125         &ahbstat_ops,
0126         NULL,               /* Funcs */
0127         0,              /* No devices yet */
0128         sizeof(struct ahbstat_priv),
0129     },
0130     &ahbstat_ids[0]
0131 };
0132 
0133 void ahbstat_register_drv (void)
0134 {
0135     drvmgr_drv_register(&ahbstat_drv_info.general);
0136 }
0137 
0138 static int ahbstat_init2(struct drvmgr_dev *dev)
0139 {
0140     struct ahbstat_priv *priv;
0141     struct amba_dev_info *ambadev;
0142 
0143     priv = dev->priv;
0144     if (!priv)
0145         return DRVMGR_NOMEM;
0146     priv->dev = dev;
0147 
0148     /* Get device information from AMBA PnP information */
0149     ambadev = (struct amba_dev_info *)dev->businfo;
0150     if (ambadev == NULL)
0151         return DRVMGR_FAIL;
0152     priv->regs = (struct ahbstat_regs *)ambadev->info.apb_slv->start;
0153     priv->minor = dev->minor_drv;
0154 
0155     strncpy(&priv->devname[0], "ahbstat0", DEVNAME_LEN);
0156     priv->devname[7] += priv->minor;
0157     /*
0158      * Initialize spinlock for AHBSTAT Device. It is used to protect user
0159      * API calls involivng priv structure from updates in ISR.
0160      */
0161     SPIN_INIT(&priv->devlock, priv->devname);
0162 
0163     /* Initialize hardware */
0164     REG_WRITE(&priv->regs->status, 0);
0165 
0166     /* Install IRQ handler */
0167     drvmgr_interrupt_register(dev, 0, priv->devname, ahbstat_isr, priv);
0168 
0169     return DRVMGR_OK;
0170 }
0171 
0172 void ahbstat_isr(void *arg)
0173 {
0174     struct ahbstat_priv *priv = arg;
0175     uint32_t fadr, status;
0176     int rc;
0177     SPIN_ISR_IRQFLAGS(lock_context);
0178 
0179     /* Get hardware status */
0180     status = REG_READ(&priv->regs->status);
0181     if ((status & AHBSTAT_STS_NE) == 0)
0182         return;
0183 
0184     /* IRQ generated by AHBSTAT core... handle it here */
0185 
0186     /* Get Failing address */
0187     fadr = REG_READ(&priv->regs->failing);
0188 
0189     SPIN_LOCK(&priv->devlock, lock_context);
0190     priv->last_status = status;
0191     priv->last_address = fadr;
0192     SPIN_UNLOCK(&priv->devlock, lock_context);
0193 
0194     /* Let user handle error, default to print the error and reenable HW
0195      *
0196      * User return 
0197      *  0: print error and reenable AHBSTAT
0198      *  1: just reenable AHBSTAT
0199      *  2: just print error
0200      *  3: do nothing
0201      */
0202     rc = 0;
0203     if (ahbstat_error != NULL)
0204         rc = ahbstat_error(priv->minor, priv->regs, status, fadr);
0205 
0206     if ((rc & 0x1) == 0) {
0207         printk("\n### AHBSTAT: %s %s error of size %" PRId32
0208             " by master %" PRId32 " at 0x%08" PRIx32 "\n",
0209             status & AHBSTAT_STS_CE ? "single" : "non-correctable",
0210             status & AHBSTAT_STS_HW ? "write" : "read",
0211             (status & AHBSTAT_STS_HS) >> AHBSTAT_STS_HS_BIT,
0212             (status & AHBSTAT_STS_HM) >> AHBSTAT_STS_HM_BIT,
0213             fadr);
0214     }
0215 
0216     if ((rc & 0x2) == 0) {
0217         /* Trigger new interrupts */
0218         REG_WRITE(&priv->regs->status, 0);
0219     }
0220 }
0221 
0222 /* Get Last received AHB Error
0223  *
0224  * Return
0225  *   0: No error received
0226  *   1: Error Received, last status and address stored into argument pointers
0227  *  -1: No such AHBSTAT device
0228  */
0229 int ahbstat_last_error(int minor, uint32_t *status, uint32_t *address)
0230 {
0231     struct drvmgr_dev *dev;
0232     struct ahbstat_priv *priv;
0233     uint32_t last_status;
0234     uint32_t last_address;
0235     SPIN_IRQFLAGS(lock_context);
0236 
0237     if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
0238         return -1;
0239     }
0240     priv = (struct ahbstat_priv *)dev->priv;
0241 
0242     /* Read information cached by ISR */
0243     SPIN_LOCK_IRQ(&priv->devlock, lock_context);
0244     last_status = REG_READ(&priv->last_status);
0245     last_address = REG_READ(&priv->last_address);
0246     SPIN_UNLOCK_IRQ(&priv->devlock, lock_context);
0247 
0248     *status = last_status;
0249     *address = last_address;
0250 
0251     return (last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT;
0252 }
0253 
0254 /* Get AHBSTAT registers address from minor. NULL returned if no such device */
0255 struct ahbstat_regs *ahbstat_get_regs(int minor)
0256 {
0257     struct drvmgr_dev *dev;
0258     struct ahbstat_priv *priv;
0259 
0260     if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) {
0261         return NULL;
0262     }
0263     priv = (struct ahbstat_priv *)dev->priv;
0264 
0265     return priv->regs;
0266 }