linux/drivers/net/ipa/ipa_interrupt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
   4 * Copyright (C) 2018-2020 Linaro Ltd.
   5 */
   6
   7/* DOC: IPA Interrupts
   8 *
   9 * The IPA has an interrupt line distinct from the interrupt used by the GSI
  10 * code.  Whereas GSI interrupts are generally related to channel events (like
  11 * transfer completions), IPA interrupts are related to other events related
  12 * to the IPA.  Some of the IPA interrupts come from a microcontroller
  13 * embedded in the IPA.  Each IPA interrupt type can be both masked and
  14 * acknowledged independent of the others.
  15 *
  16 * Two of the IPA interrupts are initiated by the microcontroller.  A third
  17 * can be generated to signal the need for a wakeup/resume when an IPA
  18 * endpoint has been suspended.  There are other IPA events, but at this
  19 * time only these three are supported.
  20 */
  21
  22#include <linux/types.h>
  23#include <linux/interrupt.h>
  24#include <linux/pm_runtime.h>
  25
  26#include "ipa.h"
  27#include "ipa_reg.h"
  28#include "ipa_endpoint.h"
  29#include "ipa_interrupt.h"
  30
  31/**
  32 * struct ipa_interrupt - IPA interrupt information
  33 * @ipa:                IPA pointer
  34 * @irq:                Linux IRQ number used for IPA interrupts
  35 * @enabled:            Mask indicating which interrupts are enabled
  36 * @handler:            Array of handlers indexed by IPA interrupt ID
  37 */
  38struct ipa_interrupt {
  39        struct ipa *ipa;
  40        u32 irq;
  41        u32 enabled;
  42        ipa_irq_handler_t handler[IPA_IRQ_COUNT];
  43};
  44
  45/* Returns true if the interrupt type is associated with the microcontroller */
  46static bool ipa_interrupt_uc(struct ipa_interrupt *interrupt, u32 irq_id)
  47{
  48        return irq_id == IPA_IRQ_UC_0 || irq_id == IPA_IRQ_UC_1;
  49}
  50
  51/* Process a particular interrupt type that has been received */
  52static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)
  53{
  54        bool uc_irq = ipa_interrupt_uc(interrupt, irq_id);
  55        struct ipa *ipa = interrupt->ipa;
  56        u32 mask = BIT(irq_id);
  57        u32 offset;
  58
  59        /* For microcontroller interrupts, clear the interrupt right away,
  60         * "to avoid clearing unhandled interrupts."
  61         */
  62        offset = ipa_reg_irq_clr_offset(ipa->version);
  63        if (uc_irq)
  64                iowrite32(mask, ipa->reg_virt + offset);
  65
  66        if (irq_id < IPA_IRQ_COUNT && interrupt->handler[irq_id])
  67                interrupt->handler[irq_id](interrupt->ipa, irq_id);
  68
  69        /* Clearing the SUSPEND_TX interrupt also clears the register
  70         * that tells us which suspended endpoint(s) caused the interrupt,
  71         * so defer clearing until after the handler has been called.
  72         */
  73        if (!uc_irq)
  74                iowrite32(mask, ipa->reg_virt + offset);
  75}
  76
  77/* IPA IRQ handler is threaded */
  78static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
  79{
  80        struct ipa_interrupt *interrupt = dev_id;
  81        struct ipa *ipa = interrupt->ipa;
  82        u32 enabled = interrupt->enabled;
  83        struct device *dev;
  84        u32 pending;
  85        u32 offset;
  86        u32 mask;
  87        int ret;
  88
  89        dev = &ipa->pdev->dev;
  90        ret = pm_runtime_get_sync(dev);
  91        if (WARN_ON(ret < 0))
  92                goto out_power_put;
  93
  94        /* The status register indicates which conditions are present,
  95         * including conditions whose interrupt is not enabled.  Handle
  96         * only the enabled ones.
  97         */
  98        offset = ipa_reg_irq_stts_offset(ipa->version);
  99        pending = ioread32(ipa->reg_virt + offset);
 100        while ((mask = pending & enabled)) {
 101                do {
 102                        u32 irq_id = __ffs(mask);
 103
 104                        mask ^= BIT(irq_id);
 105
 106                        ipa_interrupt_process(interrupt, irq_id);
 107                } while (mask);
 108                pending = ioread32(ipa->reg_virt + offset);
 109        }
 110
 111        /* If any disabled interrupts are pending, clear them */
 112        if (pending) {
 113                dev_dbg(dev, "clearing disabled IPA interrupts 0x%08x\n",
 114                        pending);
 115                offset = ipa_reg_irq_clr_offset(ipa->version);
 116                iowrite32(pending, ipa->reg_virt + offset);
 117        }
 118out_power_put:
 119        pm_runtime_mark_last_busy(dev);
 120        (void)pm_runtime_put_autosuspend(dev);
 121
 122        return IRQ_HANDLED;
 123}
 124
 125/* Common function used to enable/disable TX_SUSPEND for an endpoint */
 126static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
 127                                          u32 endpoint_id, bool enable)
 128{
 129        struct ipa *ipa = interrupt->ipa;
 130        u32 mask = BIT(endpoint_id);
 131        u32 offset;
 132        u32 val;
 133
 134        WARN_ON(!(mask & ipa->available));
 135
 136        /* IPA version 3.0 does not support TX_SUSPEND interrupt control */
 137        if (ipa->version == IPA_VERSION_3_0)
 138                return;
 139
 140        offset = ipa_reg_irq_suspend_en_offset(ipa->version);
 141        val = ioread32(ipa->reg_virt + offset);
 142        if (enable)
 143                val |= mask;
 144        else
 145                val &= ~mask;
 146        iowrite32(val, ipa->reg_virt + offset);
 147}
 148
 149/* Enable TX_SUSPEND for an endpoint */
 150void
 151ipa_interrupt_suspend_enable(struct ipa_interrupt *interrupt, u32 endpoint_id)
 152{
 153        ipa_interrupt_suspend_control(interrupt, endpoint_id, true);
 154}
 155
 156/* Disable TX_SUSPEND for an endpoint */
 157void
 158ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id)
 159{
 160        ipa_interrupt_suspend_control(interrupt, endpoint_id, false);
 161}
 162
 163/* Clear the suspend interrupt for all endpoints that signaled it */
 164void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt)
 165{
 166        struct ipa *ipa = interrupt->ipa;
 167        u32 offset;
 168        u32 val;
 169
 170        offset = ipa_reg_irq_suspend_info_offset(ipa->version);
 171        val = ioread32(ipa->reg_virt + offset);
 172
 173        /* SUSPEND interrupt status isn't cleared on IPA version 3.0 */
 174        if (ipa->version == IPA_VERSION_3_0)
 175                return;
 176
 177        offset = ipa_reg_irq_suspend_clr_offset(ipa->version);
 178        iowrite32(val, ipa->reg_virt + offset);
 179}
 180
 181/* Simulate arrival of an IPA TX_SUSPEND interrupt */
 182void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt)
 183{
 184        ipa_interrupt_process(interrupt, IPA_IRQ_TX_SUSPEND);
 185}
 186
 187/* Add a handler for an IPA interrupt */
 188void ipa_interrupt_add(struct ipa_interrupt *interrupt,
 189                       enum ipa_irq_id ipa_irq, ipa_irq_handler_t handler)
 190{
 191        struct ipa *ipa = interrupt->ipa;
 192        u32 offset;
 193
 194        WARN_ON(ipa_irq >= IPA_IRQ_COUNT);
 195
 196        interrupt->handler[ipa_irq] = handler;
 197
 198        /* Update the IPA interrupt mask to enable it */
 199        interrupt->enabled |= BIT(ipa_irq);
 200        offset = ipa_reg_irq_en_offset(ipa->version);
 201        iowrite32(interrupt->enabled, ipa->reg_virt + offset);
 202}
 203
 204/* Remove the handler for an IPA interrupt type */
 205void
 206ipa_interrupt_remove(struct ipa_interrupt *interrupt, enum ipa_irq_id ipa_irq)
 207{
 208        struct ipa *ipa = interrupt->ipa;
 209        u32 offset;
 210
 211        WARN_ON(ipa_irq >= IPA_IRQ_COUNT);
 212
 213        /* Update the IPA interrupt mask to disable it */
 214        interrupt->enabled &= ~BIT(ipa_irq);
 215        offset = ipa_reg_irq_en_offset(ipa->version);
 216        iowrite32(interrupt->enabled, ipa->reg_virt + offset);
 217
 218        interrupt->handler[ipa_irq] = NULL;
 219}
 220
 221/* Configure the IPA interrupt framework */
 222struct ipa_interrupt *ipa_interrupt_config(struct ipa *ipa)
 223{
 224        struct device *dev = &ipa->pdev->dev;
 225        struct ipa_interrupt *interrupt;
 226        unsigned int irq;
 227        u32 offset;
 228        int ret;
 229
 230        ret = platform_get_irq_byname(ipa->pdev, "ipa");
 231        if (ret <= 0) {
 232                dev_err(dev, "DT error %d getting \"ipa\" IRQ property\n",
 233                        ret);
 234                return ERR_PTR(ret ? : -EINVAL);
 235        }
 236        irq = ret;
 237
 238        interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL);
 239        if (!interrupt)
 240                return ERR_PTR(-ENOMEM);
 241        interrupt->ipa = ipa;
 242        interrupt->irq = irq;
 243
 244        /* Start with all IPA interrupts disabled */
 245        offset = ipa_reg_irq_en_offset(ipa->version);
 246        iowrite32(0, ipa->reg_virt + offset);
 247
 248        ret = request_threaded_irq(irq, NULL, ipa_isr_thread, IRQF_ONESHOT,
 249                                   "ipa", interrupt);
 250        if (ret) {
 251                dev_err(dev, "error %d requesting \"ipa\" IRQ\n", ret);
 252                goto err_kfree;
 253        }
 254
 255        ret = enable_irq_wake(irq);
 256        if (ret) {
 257                dev_err(dev, "error %d enabling wakeup for \"ipa\" IRQ\n", ret);
 258                goto err_free_irq;
 259        }
 260
 261        return interrupt;
 262
 263err_free_irq:
 264        free_irq(interrupt->irq, interrupt);
 265err_kfree:
 266        kfree(interrupt);
 267
 268        return ERR_PTR(ret);
 269}
 270
 271/* Inverse of ipa_interrupt_config() */
 272void ipa_interrupt_deconfig(struct ipa_interrupt *interrupt)
 273{
 274        struct device *dev = &interrupt->ipa->pdev->dev;
 275        int ret;
 276
 277        ret = disable_irq_wake(interrupt->irq);
 278        if (ret)
 279                dev_err(dev, "error %d disabling \"ipa\" IRQ wakeup\n", ret);
 280        free_irq(interrupt->irq, interrupt);
 281        kfree(interrupt);
 282}
 283