]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/bus/brcmstb_gisb.c
Merge tag 'drm-for-v4.13' of git://people.freedesktop.org/~airlied/linux
[karo-tx-linux.git] / drivers / bus / brcmstb_gisb.c
index 72fe0a5a8bf3ff2ce8cb7da6820dfdba526be3f6..68ac3e93b600766fb1bf6eb308c23bc2d9097bd2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Broadcom Corporation
+ * Copyright (C) 2014-2017 Broadcom
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <linux/of.h>
 #include <linux/bitops.h>
 #include <linux/pm.h>
-
-#ifdef CONFIG_ARM
-#include <asm/bug.h>
-#include <asm/signal.h>
-#endif
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
 
 #ifdef CONFIG_MIPS
 #include <asm/traps.h>
@@ -37,8 +35,6 @@
 #define  ARB_ERR_CAP_CLEAR             (1 << 0)
 #define  ARB_ERR_CAP_STATUS_TIMEOUT    (1 << 12)
 #define  ARB_ERR_CAP_STATUS_TEA                (1 << 11)
-#define  ARB_ERR_CAP_STATUS_BS_SHIFT   (1 << 2)
-#define  ARB_ERR_CAP_STATUS_BS_MASK    0x3c
 #define  ARB_ERR_CAP_STATUS_WRITE      (1 << 1)
 #define  ARB_ERR_CAP_STATUS_VALID      (1 << 0)
 
@@ -47,7 +43,6 @@ enum {
        ARB_ERR_CAP_CLR,
        ARB_ERR_CAP_HI_ADDR,
        ARB_ERR_CAP_ADDR,
-       ARB_ERR_CAP_DATA,
        ARB_ERR_CAP_STATUS,
        ARB_ERR_CAP_MASTER,
 };
@@ -57,17 +52,24 @@ static const int gisb_offsets_bcm7038[] = {
        [ARB_ERR_CAP_CLR]       = 0x0c4,
        [ARB_ERR_CAP_HI_ADDR]   = -1,
        [ARB_ERR_CAP_ADDR]      = 0x0c8,
-       [ARB_ERR_CAP_DATA]      = 0x0cc,
        [ARB_ERR_CAP_STATUS]    = 0x0d0,
        [ARB_ERR_CAP_MASTER]    = -1,
 };
 
+static const int gisb_offsets_bcm7278[] = {
+       [ARB_TIMER]             = 0x008,
+       [ARB_ERR_CAP_CLR]       = 0x7f8,
+       [ARB_ERR_CAP_HI_ADDR]   = -1,
+       [ARB_ERR_CAP_ADDR]      = 0x7e0,
+       [ARB_ERR_CAP_STATUS]    = 0x7f0,
+       [ARB_ERR_CAP_MASTER]    = 0x7f4,
+};
+
 static const int gisb_offsets_bcm7400[] = {
        [ARB_TIMER]             = 0x00c,
        [ARB_ERR_CAP_CLR]       = 0x0c8,
        [ARB_ERR_CAP_HI_ADDR]   = -1,
        [ARB_ERR_CAP_ADDR]      = 0x0cc,
-       [ARB_ERR_CAP_DATA]      = 0x0d0,
        [ARB_ERR_CAP_STATUS]    = 0x0d4,
        [ARB_ERR_CAP_MASTER]    = 0x0d8,
 };
@@ -77,7 +79,6 @@ static const int gisb_offsets_bcm7435[] = {
        [ARB_ERR_CAP_CLR]       = 0x168,
        [ARB_ERR_CAP_HI_ADDR]   = -1,
        [ARB_ERR_CAP_ADDR]      = 0x16c,
-       [ARB_ERR_CAP_DATA]      = 0x170,
        [ARB_ERR_CAP_STATUS]    = 0x174,
        [ARB_ERR_CAP_MASTER]    = 0x178,
 };
@@ -87,7 +88,6 @@ static const int gisb_offsets_bcm7445[] = {
        [ARB_ERR_CAP_CLR]       = 0x7e4,
        [ARB_ERR_CAP_HI_ADDR]   = 0x7e8,
        [ARB_ERR_CAP_ADDR]      = 0x7ec,
-       [ARB_ERR_CAP_DATA]      = 0x7f0,
        [ARB_ERR_CAP_STATUS]    = 0x7f4,
        [ARB_ERR_CAP_MASTER]    = 0x7f8,
 };
@@ -109,9 +109,13 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg)
 {
        int offset = gdev->gisb_offsets[reg];
 
-       /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
-       if (offset == -1)
-               return 1;
+       if (offset < 0) {
+               /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
+               if (reg == ARB_ERR_CAP_MASTER)
+                       return 1;
+               else
+                       return 0;
+       }
 
        if (gdev->big_endian)
                return ioread32be(gdev->base + offset);
@@ -119,6 +123,16 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg)
                return ioread32(gdev->base + offset);
 }
 
+static u64 gisb_read_address(struct brcmstb_gisb_arb_device *gdev)
+{
+       u64 value;
+
+       value = gisb_read(gdev, ARB_ERR_CAP_ADDR);
+       value |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
+
+       return value;
+}
+
 static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
 {
        int offset = gdev->gisb_offsets[reg];
@@ -127,9 +141,9 @@ static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
                return;
 
        if (gdev->big_endian)
-               iowrite32be(val, gdev->base + reg);
+               iowrite32be(val, gdev->base + offset);
        else
-               iowrite32(val, gdev->base + reg);
+               iowrite32(val, gdev->base + offset);
 }
 
 static ssize_t gisb_arb_get_timeout(struct device *dev,
@@ -185,7 +199,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
                                        const char *reason)
 {
        u32 cap_status;
-       unsigned long arb_addr;
+       u64 arb_addr;
        u32 master;
        const char *m_name;
        char m_fmt[11];
@@ -197,10 +211,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
                return 1;
 
        /* Read the address and master */
-       arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff;
-#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
-       arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
-#endif
+       arb_addr = gisb_read_address(gdev);
        master = gisb_read(gdev, ARB_ERR_CAP_MASTER);
 
        m_name = brcmstb_gisb_master_to_str(gdev, master);
@@ -209,7 +220,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
                m_name = m_fmt;
        }
 
-       pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n",
+       pr_crit("%s: %s at 0x%llx [%c %s], core: %s\n",
                __func__, reason, arb_addr,
                cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R',
                cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "",
@@ -221,27 +232,6 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
        return 0;
 }
 
-#ifdef CONFIG_ARM
-static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
-                                    struct pt_regs *regs)
-{
-       int ret = 0;
-       struct brcmstb_gisb_arb_device *gdev;
-
-       /* iterate over each GISB arb registered handlers */
-       list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next)
-               ret |= brcmstb_gisb_arb_decode_addr(gdev, "bus error");
-       /*
-        * If it was an imprecise abort, then we need to correct the
-        * return address to be _after_ the instruction.
-       */
-       if (fsr & (1 << 10))
-               regs->ARM_pc += 4;
-
-       return ret;
-}
-#endif
-
 #ifdef CONFIG_MIPS
 static int brcmstb_bus_error_handler(struct pt_regs *regs, int is_fixup)
 {
@@ -279,6 +269,36 @@ static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+/*
+ * Dump out gisb errors on die or panic.
+ */
+static int dump_gisb_error(struct notifier_block *self, unsigned long v,
+                          void *p);
+
+static struct notifier_block gisb_die_notifier = {
+       .notifier_call = dump_gisb_error,
+};
+
+static struct notifier_block gisb_panic_notifier = {
+       .notifier_call = dump_gisb_error,
+};
+
+static int dump_gisb_error(struct notifier_block *self, unsigned long v,
+                          void *p)
+{
+       struct brcmstb_gisb_arb_device *gdev;
+       const char *reason = "panic";
+
+       if (self == &gisb_die_notifier)
+               reason = "die";
+
+       /* iterate over each GISB arb registered handlers */
+       list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next)
+               brcmstb_gisb_arb_decode_addr(gdev, reason);
+
+       return NOTIFY_DONE;
+}
+
 static DEVICE_ATTR(gisb_arb_timeout, S_IWUSR | S_IRUGO,
                gisb_arb_get_timeout, gisb_arb_set_timeout);
 
@@ -296,6 +316,7 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
        { .compatible = "brcm,bcm7445-gisb-arb", .data = gisb_offsets_bcm7445 },
        { .compatible = "brcm,bcm7435-gisb-arb", .data = gisb_offsets_bcm7435 },
        { .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 },
+       { .compatible = "brcm,bcm7278-gisb-arb", .data = gisb_offsets_bcm7278 },
        { .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 },
        { },
 };
@@ -378,14 +399,16 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
 
        list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
 
-#ifdef CONFIG_ARM
-       hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0,
-                       "imprecise external abort");
-#endif
 #ifdef CONFIG_MIPS
        board_be_handler = brcmstb_bus_error_handler;
 #endif
 
+       if (list_is_singular(&brcmstb_gisb_arb_device_list)) {
+               register_die_notifier(&gisb_die_notifier);
+               atomic_notifier_chain_register(&panic_notifier_list,
+                                              &gisb_panic_notifier);
+       }
+
        dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n",
                        gdev->base, timeout_irq, tea_irq);