diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2020-04-18 02:21:32 +0200 |
---|---|---|
committer | Florian Fainelli <f.fainelli@gmail.com> | 2020-09-07 05:10:44 +0200 |
commit | fb8a0b80c4bb4d474218d515cca0ec9b9028c6b4 (patch) | |
tree | 998d23a90f6874a82615fdbb8feeb854d2b80031 /drivers/bus | |
parent | dt-bindings: bus: Document breakpoint interrupt for gisb-arb (diff) | |
download | linux-fb8a0b80c4bb4d474218d515cca0ec9b9028c6b4.tar.xz linux-fb8a0b80c4bb4d474218d515cca0ec9b9028c6b4.zip |
bus: brcmstb_gisb: Add support for breakpoint interrupts
GISB breakpoint interrupts can be raised when a breakpoint has been
enabled to match a specific master and/or GISB register address. Being
able to print a message, similar to those done during target abort or
timeout greatly helps debug systems.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index 7579439971e3..7355fa2cb439 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -30,8 +30,22 @@ #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) #define ARB_ERR_CAP_STATUS_VALID (1 << 0) +#define ARB_BP_CAP_CLEAR (1 << 0) +#define ARB_BP_CAP_STATUS_PROT_SHIFT 14 +#define ARB_BP_CAP_STATUS_TYPE (1 << 13) +#define ARB_BP_CAP_STATUS_RSP_SHIFT 10 +#define ARB_BP_CAP_STATUS_MASK GENMASK(1, 0) +#define ARB_BP_CAP_STATUS_BS_SHIFT 2 +#define ARB_BP_CAP_STATUS_WRITE (1 << 1) +#define ARB_BP_CAP_STATUS_VALID (1 << 0) + enum { ARB_TIMER, + ARB_BP_CAP_CLR, + ARB_BP_CAP_HI_ADDR, + ARB_BP_CAP_ADDR, + ARB_BP_CAP_STATUS, + ARB_BP_CAP_MASTER, ARB_ERR_CAP_CLR, ARB_ERR_CAP_HI_ADDR, ARB_ERR_CAP_ADDR, @@ -41,6 +55,11 @@ enum { static const int gisb_offsets_bcm7038[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x0b8, + [ARB_BP_CAP_STATUS] = 0x0c0, + [ARB_BP_CAP_MASTER] = -1, [ARB_ERR_CAP_CLR] = 0x0c4, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x0c8, @@ -50,6 +69,11 @@ static const int gisb_offsets_bcm7038[] = { static const int gisb_offsets_bcm7278[] = { [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x01c, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x220, + [ARB_BP_CAP_STATUS] = 0x230, + [ARB_BP_CAP_MASTER] = 0x234, [ARB_ERR_CAP_CLR] = 0x7f8, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x7e0, @@ -59,6 +83,11 @@ static const int gisb_offsets_bcm7278[] = { static const int gisb_offsets_bcm7400[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x0b8, + [ARB_BP_CAP_STATUS] = 0x0c0, + [ARB_BP_CAP_MASTER] = 0x0c4, [ARB_ERR_CAP_CLR] = 0x0c8, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x0cc, @@ -68,6 +97,11 @@ static const int gisb_offsets_bcm7400[] = { static const int gisb_offsets_bcm7435[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x158, + [ARB_BP_CAP_STATUS] = 0x160, + [ARB_BP_CAP_MASTER] = 0x164, [ARB_ERR_CAP_CLR] = 0x168, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x16c, @@ -77,6 +111,11 @@ static const int gisb_offsets_bcm7435[] = { static const int gisb_offsets_bcm7445[] = { [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x010, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x1d8, + [ARB_BP_CAP_STATUS] = 0x1e0, + [ARB_BP_CAP_MASTER] = 0x1e4, [ARB_ERR_CAP_CLR] = 0x7e4, [ARB_ERR_CAP_HI_ADDR] = 0x7e8, [ARB_ERR_CAP_ADDR] = 0x7ec, @@ -125,6 +164,16 @@ static u64 gisb_read_address(struct brcmstb_gisb_arb_device *gdev) return value; } +static u64 gisb_read_bp_address(struct brcmstb_gisb_arb_device *gdev) +{ + u64 value; + + value = gisb_read(gdev, ARB_BP_CAP_ADDR); + value |= (u64)gisb_read(gdev, ARB_BP_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]; @@ -259,6 +308,41 @@ static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t brcmstb_gisb_bp_handler(int irq, void *dev_id) +{ + struct brcmstb_gisb_arb_device *gdev = dev_id; + const char *m_name; + u32 bp_status; + u64 arb_addr; + u32 master; + char m_fmt[11]; + + bp_status = gisb_read(gdev, ARB_BP_CAP_STATUS); + + /* Invalid captured address, bail out */ + if (!(bp_status & ARB_BP_CAP_STATUS_VALID)) + return IRQ_HANDLED; + + /* Read the address and master */ + arb_addr = gisb_read_bp_address(gdev); + master = gisb_read(gdev, ARB_BP_CAP_MASTER); + + m_name = brcmstb_gisb_master_to_str(gdev, master); + if (!m_name) { + snprintf(m_fmt, sizeof(m_fmt), "0x%08x", master); + m_name = m_fmt; + } + + pr_crit("GISB: breakpoint at 0x%llx [%c], core: %s\n", + arb_addr, bp_status & ARB_BP_CAP_STATUS_WRITE ? 'W' : 'R', + m_name); + + /* clear the GISB error */ + gisb_write(gdev, ARB_ERR_CAP_CLEAR, ARB_ERR_CAP_CLR); + + return IRQ_HANDLED; +} + /* * Dump out gisb errors on die or panic. */ @@ -317,13 +401,14 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) struct brcmstb_gisb_arb_device *gdev; const struct of_device_id *of_id; struct resource *r; - int err, timeout_irq, tea_irq; + int err, timeout_irq, tea_irq, bp_irq; unsigned int num_masters, j = 0; int i, first, last; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); timeout_irq = platform_get_irq(pdev, 0); tea_irq = platform_get_irq(pdev, 1); + bp_irq = platform_get_irq(pdev, 2); gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL); if (!gdev) @@ -356,6 +441,15 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) if (err < 0) return err; + /* Interrupt is optional */ + if (bp_irq > 0) { + err = devm_request_irq(&pdev->dev, bp_irq, + brcmstb_gisb_bp_handler, 0, pdev->name, + gdev); + if (err < 0) + return err; + } + /* If we do not have a valid mask, assume all masters are enabled */ if (of_property_read_u32(dn, "brcm,gisb-arb-master-mask", &gdev->valid_mask)) |