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
  25#include "ipa.h"
  26#include "ipa_clock.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
  58        /* For microcontroller interrupts, clear the interrupt right away,
  59         * "to avoid clearing unhandled interrupts."
  60         */
  61        if (uc_irq)
  62                iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
  63
  64        if (irq_id < IPA_IRQ_COUNT && interrupt->handler[irq_id])
  65                interrupt->handler[irq_id](interrupt->ipa, irq_id);
  66
  67        /* Clearing the SUSPEND_TX interrupt also clears the register
  68         * that tells us which suspended endpoint(s) caused the interrupt,
  69         * so defer clearing until after the handler has been called.
  70         */
  71        if (!uc_irq)
  72                iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
  73}
  74
  75/* Process all IPA interrupt types that have been signaled */
  76static void ipa_interrupt_process_all(struct ipa_interrupt *interrupt)
  77{
  78        struct ipa *ipa = interrupt->ipa;
  79        u32 enabled = interrupt->enabled;
  80        u32 mask;
  81
  82        /* The status register indicates which conditions are present,
  83         * including conditions whose interrupt is not enabled.  Handle
  84         * only the enabled ones.
  85         */
  86        mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
  87        while ((mask &= enabled)) {
  88                do {
  89                        u32 irq_id = __ffs(mask);
  90
  91                        mask ^= BIT(irq_id);
  92
  93                        ipa_interrupt_process(interrupt, irq_id);
  94                } while (mask);
  95                mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
  96        }
  97}
  98
  99/* Threaded part of the IPA IRQ handler */
 100static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
 101{
 102        struct ipa_interrupt *interrupt = dev_id;
 103
 104        ipa_clock_get(interrupt->ipa);
 105
 106        ipa_interrupt_process_all(interrupt);
 107
 108        ipa_clock_put(interrupt->ipa);
 109
 110        return IRQ_HANDLED;
 111}
 112
 113/* Hard part (i.e., "real" IRQ handler) of the IRQ handler */
 114static irqreturn_t ipa_isr(int irq, void *dev_id)
 115{
 116        struct ipa_interrupt *interrupt = dev_id;
 117        struct ipa *ipa = interrupt->ipa;
 118        u32 mask;
 119
 120        mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
 121        if (mask & interrupt->enabled)
 122                return IRQ_WAKE_THREAD;
 123
 124        /* Nothing in the mask was supposed to cause an interrupt */
 125        iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
 126
 127        dev_err(&ipa->pdev->dev, "%s: unexpected interrupt, mask 0x%08x\n",
 128                __func__, mask);
 129
 130        return IRQ_HANDLED;
 131}
 132
 133/* Common function used to enable/disable TX_SUSPEND for an endpoint */
 134static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
 135                                          u32 endpoint_id, bool enable)
 136{
 137        struct ipa *ipa = interrupt->ipa;
 138        u32 mask = BIT(endpoint_id);
 139        u32 val;
 140
 141        /* assert(mask & ipa->available); */
 142        val = ioread32(ipa->reg_virt + IPA_REG_SUSPEND_IRQ_EN_OFFSET);
 143        if (enable)
 144                val |= mask;
 145        else
 146                val &= ~mask;
 147        iowrite32(val, ipa->reg_virt + IPA_REG_SUSPEND_IRQ_EN_OFFSET);
 148}
 149
 150/* Enable TX_SUSPEND for an endpoint */
 151void
 152ipa_interrupt_suspend_enable(struct ipa_interrupt *interrupt, u32 endpoint_id)
 153{
 154        ipa_interrupt_suspend_control(interrupt, endpoint_id, true);
 155}
 156
 157/* Disable TX_SUSPEND for an endpoint */
 158void
 159ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id)
 160{
 161        ipa_interrupt_suspend_control(interrupt, endpoint_id, false);
 162}
 163
 164/* Clear the suspend interrupt for all endpoints that signaled it */
 165void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt)
 166{
 167        struct ipa *ipa = interrupt->ipa;
 168        u32 val;
 169
 170        val = ioread32(ipa->reg_virt + IPA_REG_IRQ_SUSPEND_INFO_OFFSET);
 171        iowrite32(val, ipa->reg_virt + IPA_REG_SUSPEND_IRQ_CLR_OFFSET);
 172}
 173
 174/* Simulate arrival of an IPA TX_SUSPEND interrupt */
 175void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt)
 176{
 177        ipa_interrupt_process(interrupt, IPA_IRQ_TX_SUSPEND);
 178}
 179
 180/* Add a handler for an IPA interrupt */
 181void ipa_interrupt_add(struct ipa_interrupt *interrupt,
 182                       enum ipa_irq_id ipa_irq, ipa_irq_handler_t handler)
 183{
 184        struct ipa *ipa = interrupt->ipa;
 185
 186        /* assert(ipa_irq < IPA_IRQ_COUNT); */
 187        interrupt->handler[ipa_irq] = handler;
 188
 189        /* Update the IPA interrupt mask to enable it */
 190        interrupt->enabled |= BIT(ipa_irq);
 191        iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
 192}
 193
 194/* Remove the handler for an IPA interrupt type */
 195void
 196ipa_interrupt_remove(struct ipa_interrupt *interrupt, enum ipa_irq_id ipa_irq)
 197{
 198        struct ipa *ipa = interrupt->ipa;
 199
 200        /* assert(ipa_irq < IPA_IRQ_COUNT); */
 201        /* Update the IPA interrupt mask to disable it */
 202        interrupt->enabled &= ~BIT(ipa_irq);
 203        iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
 204
 205        interrupt->handler[ipa_irq] = NULL;
 206}
 207
 208/* Set up the IPA interrupt framework */
 209struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
 210{
 211        struct device *dev = &ipa->pdev->dev;
 212        struct ipa_interrupt *interrupt;
 213        unsigned int irq;
 214        int ret;
 215
 216        ret = platform_get_irq_byname(ipa->pdev, "ipa");
 217        if (ret <= 0) {
 218                dev_err(dev, "DT error %d getting \"ipa\" IRQ property\n",
 219                        ret);
 220                return ERR_PTR(ret ? : -EINVAL);
 221        }
 222        irq = ret;
 223
 224        interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL);
 225        if (!interrupt)
 226                return ERR_PTR(-ENOMEM);
 227        interrupt->ipa = ipa;
 228        interrupt->irq = irq;
 229
 230        /* Start with all IPA interrupts disabled */
 231        iowrite32(0, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
 232
 233        ret = request_threaded_irq(irq, ipa_isr, ipa_isr_thread, IRQF_ONESHOT,
 234                                   "ipa", interrupt);
 235        if (ret) {
 236                dev_err(dev, "error %d requesting \"ipa\" IRQ\n", ret);
 237                goto err_kfree;
 238        }
 239
 240        return interrupt;
 241
 242err_kfree:
 243        kfree(interrupt);
 244
 245        return ERR_PTR(ret);
 246}
 247
 248/* Tear down the IPA interrupt framework */
 249void ipa_interrupt_teardown(struct ipa_interrupt *interrupt)
 250{
 251        free_irq(interrupt->irq, interrupt);
 252        kfree(interrupt);
 253}
 254