mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
net: ipa: support IPA interrupt addresses for IPA v4.7
Starting with IPA v4.7, registers related to IPA interrupts are located at a fixed offset 0x1000 above than the addresses used for earlier versions. Define and use functions to provide the offset to use for these registers based on IPA version. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
cc5199ed50
commit
e666aa978a
3 changed files with 146 additions and 48 deletions
|
@ -54,12 +54,14 @@ 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;
|
||||
u32 mask = BIT(irq_id);
|
||||
u32 offset;
|
||||
|
||||
/* For microcontroller interrupts, clear the interrupt right away,
|
||||
* "to avoid clearing unhandled interrupts."
|
||||
*/
|
||||
offset = ipa_reg_irq_clr_offset(ipa->version);
|
||||
if (uc_irq)
|
||||
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
|
||||
iowrite32(mask, ipa->reg_virt + offset);
|
||||
|
||||
if (irq_id < IPA_IRQ_COUNT && interrupt->handler[irq_id])
|
||||
interrupt->handler[irq_id](interrupt->ipa, irq_id);
|
||||
|
@ -69,7 +71,7 @@ static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)
|
|||
* so defer clearing until after the handler has been called.
|
||||
*/
|
||||
if (!uc_irq)
|
||||
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
|
||||
iowrite32(mask, ipa->reg_virt + offset);
|
||||
}
|
||||
|
||||
/* Process all IPA interrupt types that have been signaled */
|
||||
|
@ -77,13 +79,15 @@ static void ipa_interrupt_process_all(struct ipa_interrupt *interrupt)
|
|||
{
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 enabled = interrupt->enabled;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
||||
/* The status register indicates which conditions are present,
|
||||
* including conditions whose interrupt is not enabled. Handle
|
||||
* only the enabled ones.
|
||||
*/
|
||||
mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
|
||||
offset = ipa_reg_irq_stts_offset(ipa->version);
|
||||
mask = ioread32(ipa->reg_virt + offset);
|
||||
while ((mask &= enabled)) {
|
||||
do {
|
||||
u32 irq_id = __ffs(mask);
|
||||
|
@ -92,7 +96,7 @@ static void ipa_interrupt_process_all(struct ipa_interrupt *interrupt)
|
|||
|
||||
ipa_interrupt_process(interrupt, irq_id);
|
||||
} while (mask);
|
||||
mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
|
||||
mask = ioread32(ipa->reg_virt + offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,14 +119,17 @@ static irqreturn_t ipa_isr(int irq, void *dev_id)
|
|||
{
|
||||
struct ipa_interrupt *interrupt = dev_id;
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
||||
mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
|
||||
offset = ipa_reg_irq_stts_offset(ipa->version);
|
||||
mask = ioread32(ipa->reg_virt + offset);
|
||||
if (mask & interrupt->enabled)
|
||||
return IRQ_WAKE_THREAD;
|
||||
|
||||
/* Nothing in the mask was supposed to cause an interrupt */
|
||||
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
|
||||
offset = ipa_reg_irq_clr_offset(ipa->version);
|
||||
iowrite32(mask, ipa->reg_virt + offset);
|
||||
|
||||
dev_err(&ipa->pdev->dev, "%s: unexpected interrupt, mask 0x%08x\n",
|
||||
__func__, mask);
|
||||
|
@ -136,15 +143,22 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
|
|||
{
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 mask = BIT(endpoint_id);
|
||||
u32 offset;
|
||||
u32 val;
|
||||
|
||||
/* assert(mask & ipa->available); */
|
||||
val = ioread32(ipa->reg_virt + IPA_REG_IRQ_SUSPEND_EN_OFFSET);
|
||||
|
||||
/* IPA version 3.0 does not support TX_SUSPEND interrupt control */
|
||||
if (ipa->version == IPA_VERSION_3_0)
|
||||
return;
|
||||
|
||||
offset = ipa_reg_irq_suspend_en_offset(ipa->version);
|
||||
val = ioread32(ipa->reg_virt + offset);
|
||||
if (enable)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
iowrite32(val, ipa->reg_virt + IPA_REG_IRQ_SUSPEND_EN_OFFSET);
|
||||
iowrite32(val, ipa->reg_virt + offset);
|
||||
}
|
||||
|
||||
/* Enable TX_SUSPEND for an endpoint */
|
||||
|
@ -165,10 +179,18 @@ ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id)
|
|||
void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt)
|
||||
{
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 offset;
|
||||
u32 val;
|
||||
|
||||
val = ioread32(ipa->reg_virt + IPA_REG_IRQ_SUSPEND_INFO_OFFSET);
|
||||
iowrite32(val, ipa->reg_virt + IPA_REG_IRQ_SUSPEND_CLR_OFFSET);
|
||||
offset = ipa_reg_irq_suspend_info_offset(ipa->version);
|
||||
val = ioread32(ipa->reg_virt + offset);
|
||||
|
||||
/* SUSPEND interrupt status isn't cleared on IPA version 3.0 */
|
||||
if (ipa->version == IPA_VERSION_3_0)
|
||||
return;
|
||||
|
||||
offset = ipa_reg_irq_suspend_clr_offset(ipa->version);
|
||||
iowrite32(val, ipa->reg_virt + offset);
|
||||
}
|
||||
|
||||
/* Simulate arrival of an IPA TX_SUSPEND interrupt */
|
||||
|
@ -182,13 +204,15 @@ void ipa_interrupt_add(struct ipa_interrupt *interrupt,
|
|||
enum ipa_irq_id ipa_irq, ipa_irq_handler_t handler)
|
||||
{
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 offset;
|
||||
|
||||
/* assert(ipa_irq < IPA_IRQ_COUNT); */
|
||||
interrupt->handler[ipa_irq] = handler;
|
||||
|
||||
/* Update the IPA interrupt mask to enable it */
|
||||
interrupt->enabled |= BIT(ipa_irq);
|
||||
iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
|
||||
offset = ipa_reg_irq_en_offset(ipa->version);
|
||||
iowrite32(interrupt->enabled, ipa->reg_virt + offset);
|
||||
}
|
||||
|
||||
/* Remove the handler for an IPA interrupt type */
|
||||
|
@ -196,11 +220,13 @@ void
|
|||
ipa_interrupt_remove(struct ipa_interrupt *interrupt, enum ipa_irq_id ipa_irq)
|
||||
{
|
||||
struct ipa *ipa = interrupt->ipa;
|
||||
u32 offset;
|
||||
|
||||
/* assert(ipa_irq < IPA_IRQ_COUNT); */
|
||||
/* Update the IPA interrupt mask to disable it */
|
||||
interrupt->enabled &= ~BIT(ipa_irq);
|
||||
iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
|
||||
offset = ipa_reg_irq_en_offset(ipa->version);
|
||||
iowrite32(interrupt->enabled, ipa->reg_virt + offset);
|
||||
|
||||
interrupt->handler[ipa_irq] = NULL;
|
||||
}
|
||||
|
@ -211,6 +237,7 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
|
|||
struct device *dev = &ipa->pdev->dev;
|
||||
struct ipa_interrupt *interrupt;
|
||||
unsigned int irq;
|
||||
u32 offset;
|
||||
int ret;
|
||||
|
||||
ret = platform_get_irq_byname(ipa->pdev, "ipa");
|
||||
|
@ -228,7 +255,8 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
|
|||
interrupt->irq = irq;
|
||||
|
||||
/* Start with all IPA interrupts disabled */
|
||||
iowrite32(0, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
|
||||
offset = ipa_reg_irq_en_offset(ipa->version);
|
||||
iowrite32(0, ipa->reg_virt + offset);
|
||||
|
||||
ret = request_threaded_irq(irq, ipa_isr, ipa_isr_thread, IRQF_ONESHOT,
|
||||
"ipa", interrupt);
|
||||
|
|
|
@ -717,20 +717,46 @@ enum ipa_seq_rep_type {
|
|||
#define ROUTER_HASH_MSK_METADATA_FMASK GENMASK(22, 22)
|
||||
#define IPA_REG_ENDP_ROUTER_HASH_MSK_ALL GENMASK(22, 16)
|
||||
|
||||
#define IPA_REG_IRQ_STTS_OFFSET \
|
||||
IPA_REG_IRQ_STTS_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_STTS_EE_N_OFFSET(ee) \
|
||||
(0x00003008 + 0x1000 * (ee))
|
||||
static inline u32 ipa_reg_irq_stts_ee_n_offset(enum ipa_version version,
|
||||
u32 ee)
|
||||
{
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x00003008 + 0x1000 * ee;
|
||||
|
||||
#define IPA_REG_IRQ_EN_OFFSET \
|
||||
IPA_REG_IRQ_EN_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_EN_EE_N_OFFSET(ee) \
|
||||
(0x0000300c + 0x1000 * (ee))
|
||||
return 0x00004008 + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_stts_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_stts_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_en_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x0000300c + 0x1000 * ee;
|
||||
|
||||
return 0x0000400c + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_en_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_en_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_clr_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x00003010 + 0x1000 * ee;
|
||||
|
||||
return 0x00004010 + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_clr_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_clr_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
#define IPA_REG_IRQ_CLR_OFFSET \
|
||||
IPA_REG_IRQ_CLR_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_CLR_EE_N_OFFSET(ee) \
|
||||
(0x00003010 + 0x1000 * (ee))
|
||||
/**
|
||||
* enum ipa_irq_id - Bit positions representing type of IPA IRQ
|
||||
* @IPA_IRQ_UC_0: Microcontroller event interrupt
|
||||
|
@ -806,32 +832,75 @@ enum ipa_irq_id {
|
|||
IPA_IRQ_COUNT, /* Last; not an id */
|
||||
};
|
||||
|
||||
#define IPA_REG_IRQ_UC_OFFSET \
|
||||
IPA_REG_IRQ_UC_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_UC_EE_N_OFFSET(ee) \
|
||||
(0x0000301c + 0x1000 * (ee))
|
||||
static inline u32 ipa_reg_irq_uc_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x0000301c + 0x1000 * ee;
|
||||
|
||||
return 0x0000401c + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32 ipa_reg_irq_uc_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_uc_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
#define UC_INTR_FMASK GENMASK(0, 0)
|
||||
|
||||
/* ipa->available defines the valid bits in the SUSPEND_INFO register */
|
||||
#define IPA_REG_IRQ_SUSPEND_INFO_OFFSET \
|
||||
IPA_REG_IRQ_SUSPEND_INFO_EE_N_OFFSET(GSI_EE_AP)
|
||||
/* This register is at (0x00003098 + 0x1000 * (ee)) for IPA v3.0 */
|
||||
#define IPA_REG_IRQ_SUSPEND_INFO_EE_N_OFFSET(ee) \
|
||||
(0x00003030 + 0x1000 * (ee))
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_info_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
if (version == IPA_VERSION_3_0)
|
||||
return 0x00003098 + 0x1000 * ee;
|
||||
|
||||
/* ipa->available defines the valid bits in the IRQ_SUSPEND_EN register */
|
||||
/* This register is present for IPA v3.1+ */
|
||||
#define IPA_REG_IRQ_SUSPEND_EN_OFFSET \
|
||||
IPA_REG_IRQ_SUSPEND_EN_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_SUSPEND_EN_EE_N_OFFSET(ee) \
|
||||
(0x00003034 + 0x1000 * (ee))
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x00003030 + 0x1000 * ee;
|
||||
|
||||
/* ipa->available defines the valid bits in the IRQ_SUSPEND_CLR register */
|
||||
/* This register is present for IPA v3.1+ */
|
||||
#define IPA_REG_IRQ_SUSPEND_CLR_OFFSET \
|
||||
IPA_REG_IRQ_SUSPEND_CLR_EE_N_OFFSET(GSI_EE_AP)
|
||||
#define IPA_REG_IRQ_SUSPEND_CLR_EE_N_OFFSET(ee) \
|
||||
(0x00003038 + 0x1000 * (ee))
|
||||
return 0x00004030 + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_info_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_suspend_info_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
/* ipa->available defines the valid bits in the SUSPEND_EN register */
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_en_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
/* assert(version != IPA_VERSION_3_0); */
|
||||
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x00003034 + 0x1000 * ee;
|
||||
|
||||
return 0x00004034 + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_en_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_suspend_en_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
/* ipa->available defines the valid bits in the SUSPEND_CLR register */
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_clr_ee_n_offset(enum ipa_version version, u32 ee)
|
||||
{
|
||||
/* assert(version != IPA_VERSION_3_0); */
|
||||
|
||||
if (version < IPA_VERSION_4_9)
|
||||
return 0x00003038 + 0x1000 * ee;
|
||||
|
||||
return 0x00004038 + 0x1000 * ee;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
ipa_reg_irq_suspend_clr_offset(enum ipa_version version)
|
||||
{
|
||||
return ipa_reg_irq_suspend_clr_ee_n_offset(version, GSI_EE_AP);
|
||||
}
|
||||
|
||||
int ipa_reg_init(struct ipa *ipa);
|
||||
void ipa_reg_exit(struct ipa *ipa);
|
||||
|
|
|
@ -192,6 +192,7 @@ void ipa_uc_teardown(struct ipa *ipa)
|
|||
static void send_uc_command(struct ipa *ipa, u32 command, u32 command_param)
|
||||
{
|
||||
struct ipa_uc_mem_area *shared = ipa_uc_shared(ipa);
|
||||
u32 offset;
|
||||
u32 val;
|
||||
|
||||
/* Fill in the command data */
|
||||
|
@ -203,8 +204,8 @@ static void send_uc_command(struct ipa *ipa, u32 command, u32 command_param)
|
|||
|
||||
/* Use an interrupt to tell the microcontroller the command is ready */
|
||||
val = u32_encode_bits(1, UC_INTR_FMASK);
|
||||
|
||||
iowrite32(val, ipa->reg_virt + IPA_REG_IRQ_UC_OFFSET);
|
||||
offset = ipa_reg_irq_uc_offset(ipa->version);
|
||||
iowrite32(val, ipa->reg_virt + offset);
|
||||
}
|
||||
|
||||
/* Tell the microcontroller the AP is shutting down */
|
||||
|
|
Loading…
Reference in a new issue