Commit 0da68553 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-ipa-simplify-ipa-interrupt-handling'

Alex Elder says:

====================
net: ipa: simplify IPA interrupt handling

One of the IPA's two IRQs fires when data on a suspended channel is
available (to request that the channel--or system--be resumed to
recieve the pending data).  This interrupt also handles a few
conditions signaled by the embedded microcontroller.

For this "IPA interrupt", the current code requires a handler to be
dynamically registered for each interrupt condition.  Any condition
that has no registered handler is quietly ignored.  This design is
derived from the downstream IPA driver implementation.

There isn't any need for this complexity.  Even in the downstream
code, only four of the available 30 or so IPA interrupt conditions
are ever handled.  So these handlers can pretty easily just be
called directly in the main IRQ handler function.

This series simplifies the interrupt handling code by having the
small number of IPA interrupt handlers be called directly, rather
than having them be registered dynamically.

Version 2 just adds a missing forward-reference, as suggested by
Caleb.
====================

Link: https://lore.kernel.org/r/20230104175233.2862874-1-elder@linaro.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents afd50da9 bfb79854
......@@ -26,6 +26,8 @@
#include "ipa.h"
#include "ipa_reg.h"
#include "ipa_endpoint.h"
#include "ipa_power.h"
#include "ipa_uc.h"
#include "ipa_interrupt.h"
/**
......@@ -33,47 +35,47 @@
* @ipa: IPA pointer
* @irq: Linux IRQ number used for IPA interrupts
* @enabled: Mask indicating which interrupts are enabled
* @handler: Array of handlers indexed by IPA interrupt ID
*/
struct ipa_interrupt {
struct ipa *ipa;
u32 irq;
u32 enabled;
ipa_irq_handler_t handler[IPA_IRQ_COUNT];
};
/* Returns true if the interrupt type is associated with the microcontroller */
static bool ipa_interrupt_uc(struct ipa_interrupt *interrupt, u32 irq_id)
{
return irq_id == IPA_IRQ_UC_0 || irq_id == IPA_IRQ_UC_1;
}
/* Process a particular interrupt type that has been received */
static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)
{
bool uc_irq = ipa_interrupt_uc(interrupt, irq_id);
struct ipa *ipa = interrupt->ipa;
const struct ipa_reg *reg;
u32 mask = BIT(irq_id);
u32 offset;
/* For microcontroller interrupts, clear the interrupt right away,
* "to avoid clearing unhandled interrupts."
*/
reg = ipa_reg(ipa, IPA_IRQ_CLR);
offset = ipa_reg_offset(reg);
if (uc_irq)
iowrite32(mask, ipa->reg_virt + offset);
if (irq_id < IPA_IRQ_COUNT && interrupt->handler[irq_id])
interrupt->handler[irq_id](interrupt->ipa, irq_id);
/* Clearing the SUSPEND_TX interrupt also clears the register
* that tells us which suspended endpoint(s) caused the interrupt,
* so defer clearing until after the handler has been called.
switch (irq_id) {
case IPA_IRQ_UC_0:
case IPA_IRQ_UC_1:
/* For microcontroller interrupts, clear the interrupt right
* away, "to avoid clearing unhandled interrupts."
*/
if (!uc_irq)
iowrite32(mask, ipa->reg_virt + offset);
ipa_uc_interrupt_handler(ipa, irq_id);
break;
case IPA_IRQ_TX_SUSPEND:
/* Clearing the SUSPEND_TX interrupt also clears the
* register that tells us which suspended endpoint(s)
* caused the interrupt, so defer clearing until after
* the handler has been called.
*/
ipa_power_suspend_handler(ipa, irq_id);
fallthrough;
default: /* Silently ignore (and clear) any other condition */
iowrite32(mask, ipa->reg_virt + offset);
break;
}
}
/* IPA IRQ handler is threaded */
......@@ -127,6 +129,29 @@ static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void ipa_interrupt_enabled_update(struct ipa *ipa)
{
const struct ipa_reg *reg = ipa_reg(ipa, IPA_IRQ_EN);
iowrite32(ipa->interrupt->enabled, ipa->reg_virt + ipa_reg_offset(reg));
}
/* Enable an IPA interrupt type */
void ipa_interrupt_enable(struct ipa *ipa, enum ipa_irq_id ipa_irq)
{
/* Update the IPA interrupt mask to enable it */
ipa->interrupt->enabled |= BIT(ipa_irq);
ipa_interrupt_enabled_update(ipa);
}
/* Disable an IPA interrupt type */
void ipa_interrupt_disable(struct ipa *ipa, enum ipa_irq_id ipa_irq)
{
/* Update the IPA interrupt mask to disable it */
ipa->interrupt->enabled &= ~BIT(ipa_irq);
ipa_interrupt_enabled_update(ipa);
}
/* Common function used to enable/disable TX_SUSPEND for an endpoint */
static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
u32 endpoint_id, bool enable)
......@@ -200,44 +225,6 @@ void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt)
ipa_interrupt_process(interrupt, IPA_IRQ_TX_SUSPEND);
}
/* Add a handler for an IPA interrupt */
void ipa_interrupt_add(struct ipa_interrupt *interrupt,
enum ipa_irq_id ipa_irq, ipa_irq_handler_t handler)
{
struct ipa *ipa = interrupt->ipa;
const struct ipa_reg *reg;
if (WARN_ON(ipa_irq >= IPA_IRQ_COUNT))
return;
interrupt->handler[ipa_irq] = handler;
/* Update the IPA interrupt mask to enable it */
interrupt->enabled |= BIT(ipa_irq);
reg = ipa_reg(ipa, IPA_IRQ_EN);
iowrite32(interrupt->enabled, ipa->reg_virt + ipa_reg_offset(reg));
}
/* Remove the handler for an IPA interrupt type */
void
ipa_interrupt_remove(struct ipa_interrupt *interrupt, enum ipa_irq_id ipa_irq)
{
struct ipa *ipa = interrupt->ipa;
const struct ipa_reg *reg;
if (WARN_ON(ipa_irq >= IPA_IRQ_COUNT))
return;
/* Update the IPA interrupt mask to disable it */
interrupt->enabled &= ~BIT(ipa_irq);
reg = ipa_reg(ipa, IPA_IRQ_EN);
iowrite32(interrupt->enabled, ipa->reg_virt + ipa_reg_offset(reg));
interrupt->handler[ipa_irq] = NULL;
}
/* Configure the IPA interrupt framework */
struct ipa_interrupt *ipa_interrupt_config(struct ipa *ipa)
{
......
......@@ -11,39 +11,7 @@
struct ipa;
struct ipa_interrupt;
/**
* typedef ipa_irq_handler_t - IPA interrupt handler function type
* @ipa: IPA pointer
* @irq_id: interrupt type
*
* Callback function registered by ipa_interrupt_add() to handle a specific
* IPA interrupt type
*/
typedef void (*ipa_irq_handler_t)(struct ipa *ipa, enum ipa_irq_id irq_id);
/**
* ipa_interrupt_add() - Register a handler for an IPA interrupt type
* @interrupt: IPA interrupt structure
* @irq_id: IPA interrupt type
* @handler: Handler function for the interrupt
*
* Add a handler for an IPA interrupt and enable it. IPA interrupt
* handlers are run in threaded interrupt context, so are allowed to
* block.
*/
void ipa_interrupt_add(struct ipa_interrupt *interrupt, enum ipa_irq_id irq_id,
ipa_irq_handler_t handler);
/**
* ipa_interrupt_remove() - Remove the handler for an IPA interrupt type
* @interrupt: IPA interrupt structure
* @irq_id: IPA interrupt type
*
* Remove an IPA interrupt handler and disable it.
*/
void ipa_interrupt_remove(struct ipa_interrupt *interrupt,
enum ipa_irq_id irq_id);
enum ipa_irq_id;
/**
* ipa_interrupt_suspend_enable - Enable TX_SUSPEND for an endpoint
......@@ -85,6 +53,20 @@ void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt);
*/
void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt);
/**
* ipa_interrupt_enable() - Enable an IPA interrupt type
* @ipa: IPA pointer
* @ipa_irq: IPA interrupt ID
*/
void ipa_interrupt_enable(struct ipa *ipa, enum ipa_irq_id ipa_irq);
/**
* ipa_interrupt_disable() - Disable an IPA interrupt type
* @ipa: IPA pointer
* @ipa_irq: IPA interrupt ID
*/
void ipa_interrupt_disable(struct ipa *ipa, enum ipa_irq_id ipa_irq);
/**
* ipa_interrupt_config() - Configure the IPA interrupt framework
* @ipa: IPA pointer
......
......@@ -202,17 +202,7 @@ u32 ipa_core_clock_rate(struct ipa *ipa)
return ipa->power ? (u32)clk_get_rate(ipa->power->core) : 0;
}
/**
* ipa_suspend_handler() - Handle the suspend IPA interrupt
* @ipa: IPA pointer
* @irq_id: IPA interrupt type (unused)
*
* If an RX endpoint is suspended, and the IPA has a packet destined for
* that endpoint, the IPA generates a SUSPEND interrupt to inform the AP
* that it should resume the endpoint. If we get one of these interrupts
* we just wake up the system.
*/
static void ipa_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
void ipa_power_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
{
/* To handle an IPA interrupt we will have resumed the hardware
* just to handle the interrupt, so we're done. If we are in a
......@@ -335,12 +325,11 @@ int ipa_power_setup(struct ipa *ipa)
{
int ret;
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_TX_SUSPEND,
ipa_suspend_handler);
ipa_interrupt_enable(ipa, IPA_IRQ_TX_SUSPEND);
ret = device_init_wakeup(&ipa->pdev->dev, true);
if (ret)
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
ipa_interrupt_disable(ipa, IPA_IRQ_TX_SUSPEND);
return ret;
}
......@@ -348,7 +337,7 @@ int ipa_power_setup(struct ipa *ipa)
void ipa_power_teardown(struct ipa *ipa)
{
(void)device_init_wakeup(&ipa->pdev->dev, false);
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
ipa_interrupt_disable(ipa, IPA_IRQ_TX_SUSPEND);
}
/* Initialize IPA power management */
......
......@@ -10,6 +10,7 @@ struct device;
struct ipa;
struct ipa_power_data;
enum ipa_irq_id;
/* IPA device power management function block */
extern const struct dev_pm_ops ipa_pm_ops;
......@@ -47,6 +48,17 @@ void ipa_power_modem_queue_active(struct ipa *ipa);
*/
void ipa_power_retention(struct ipa *ipa, bool enable);
/**
* ipa_power_suspend_handler() - Handler for SUSPEND IPA interrupts
* @ipa: IPA pointer
* @irq_id: IPA interrupt ID (unused)
*
* If an RX endpoint is suspended, and the IPA has a packet destined for
* that endpoint, the IPA generates a SUSPEND interrupt to inform the AP
* that it should resume the endpoint.
*/
void ipa_power_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id);
/**
* ipa_power_setup() - Set up IPA power management
* @ipa: IPA pointer
......
......@@ -124,7 +124,7 @@ static struct ipa_uc_mem_area *ipa_uc_shared(struct ipa *ipa)
}
/* Microcontroller event IPA interrupt handler */
static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
static void ipa_uc_event_handler(struct ipa *ipa)
{
struct ipa_uc_mem_area *shared = ipa_uc_shared(ipa);
struct device *dev = &ipa->pdev->dev;
......@@ -138,7 +138,7 @@ static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
}
/* Microcontroller response IPA interrupt handler */
static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
static void ipa_uc_response_hdlr(struct ipa *ipa)
{
struct ipa_uc_mem_area *shared = ipa_uc_shared(ipa);
struct device *dev = &ipa->pdev->dev;
......@@ -170,13 +170,22 @@ static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
}
}
void ipa_uc_interrupt_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
{
/* Silently ignore anything unrecognized */
if (irq_id == IPA_IRQ_UC_0)
ipa_uc_event_handler(ipa);
else if (irq_id == IPA_IRQ_UC_1)
ipa_uc_response_hdlr(ipa);
}
/* Configure the IPA microcontroller subsystem */
void ipa_uc_config(struct ipa *ipa)
{
ipa->uc_powered = false;
ipa->uc_loaded = false;
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_0, ipa_uc_event_handler);
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_1, ipa_uc_response_hdlr);
ipa_interrupt_enable(ipa, IPA_IRQ_UC_0);
ipa_interrupt_enable(ipa, IPA_IRQ_UC_1);
}
/* Inverse of ipa_uc_config() */
......@@ -184,8 +193,8 @@ void ipa_uc_deconfig(struct ipa *ipa)
{
struct device *dev = &ipa->pdev->dev;
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_1);
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_0);
ipa_interrupt_disable(ipa, IPA_IRQ_UC_1);
ipa_interrupt_disable(ipa, IPA_IRQ_UC_0);
if (ipa->uc_loaded)
ipa_power_retention(ipa, false);
......
......@@ -7,6 +7,14 @@
#define _IPA_UC_H_
struct ipa;
enum ipa_irq_id;
/**
* ipa_uc_interrupt_handler() - Handler for microcontroller IPA interrupts
* @ipa: IPA pointer
* @irq_id: IPA interrupt ID
*/
void ipa_uc_interrupt_handler(struct ipa *ipa, enum ipa_irq_id irq_id);
/**
* ipa_uc_config() - Configure the IPA microcontroller subsystem
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment