412 lines
12 KiB
C
Executable file
412 lines
12 KiB
C
Executable file
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2014 - 2021 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* Uses */
|
|
#include <linux/bitmap.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/bitmap.h>
|
|
#include <scsc/scsc_logring.h>
|
|
#include "scsc_mif_abs.h"
|
|
|
|
/* Implements */
|
|
#include "mifintrbit.h"
|
|
|
|
/* default handler just logs a warning and clears the bit */
|
|
static void mifintrbit_default_handler(int irq, void *data)
|
|
{
|
|
struct mifintrbit *intr = (struct mifintrbit *)data;
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
intr->mif->irq_bit_clear(intr->mif, irq, intr->target);
|
|
#else
|
|
intr->mif->irq_bit_clear(intr->mif, irq);
|
|
#endif
|
|
}
|
|
|
|
static void print_bitmaps(struct mifintrbit *intr)
|
|
{
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
unsigned long dst1, dst2;
|
|
|
|
bitmap_copy_le(&dst1, intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
bitmap_copy_le(&dst2, intr->bitmap_fromhost, MIFINTRBIT_NUM_INT);
|
|
#else
|
|
unsigned long dst1, dst2, dst3;
|
|
|
|
bitmap_copy_le(&dst1, intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
bitmap_copy_le(&dst2, intr->bitmap_fromhost_wlan, MIFINTRBIT_NUM_INT);
|
|
bitmap_copy_le(&dst3, intr->bitmap_fromhost_fxm_1, MIFINTRBIT_NUM_INT);
|
|
#endif
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
static void mifiintrman_isr_wpan(int irq, void *data)
|
|
{
|
|
struct mifintrbit *intr = (struct mifintrbit *)data;
|
|
unsigned long flags;
|
|
unsigned long int irq_reg = 0;
|
|
int bit;
|
|
|
|
/* Avoid unused parameter error */
|
|
(void)irq;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
irq_reg = intr->mif->irq_get(intr->mif, SCSC_MIF_ABS_TARGET_WPAN);
|
|
|
|
print_bitmaps(intr);
|
|
for_each_set_bit(bit, &irq_reg, MIFINTRBIT_NUM_INT) {
|
|
if (intr->mifintrbit_irq_handler[bit] != mifintrbit_default_handler)
|
|
intr->mifintrbit_irq_handler[bit](bit, intr->irq_data[bit]);
|
|
else
|
|
intr->mif->irq_bit_clear(intr->mif, bit, SCSC_MIF_ABS_TARGET_WPAN);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
}
|
|
#endif
|
|
|
|
static void mifiintrman_isr(int irq, void *data)
|
|
{
|
|
struct mifintrbit *intr = (struct mifintrbit *)data;
|
|
unsigned long flags;
|
|
unsigned long int irq_reg = 0;
|
|
int bit;
|
|
|
|
/* Avoid unused parameter error */
|
|
(void)irq;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
irq_reg = intr->mif->irq_get(intr->mif, SCSC_MIF_ABS_TARGET_WLAN);
|
|
#else
|
|
irq_reg = intr->mif->irq_get(intr->mif);
|
|
#endif
|
|
|
|
print_bitmaps(intr);
|
|
for_each_set_bit(bit, &irq_reg, MIFINTRBIT_NUM_INT) {
|
|
if (intr->mifintrbit_irq_handler[bit] != mifintrbit_default_handler)
|
|
intr->mifintrbit_irq_handler[bit](bit, intr->irq_data[bit]);
|
|
else
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
intr->mif->irq_bit_clear(intr->mif, bit, SCSC_MIF_ABS_TARGET_WLAN);
|
|
#else
|
|
intr->mif->irq_bit_clear(intr->mif, bit);
|
|
#endif
|
|
}
|
|
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
}
|
|
|
|
/* Public functions */
|
|
int mifintrbit_alloc_tohost(struct mifintrbit *intr, mifintrbit_handler handler, void *data)
|
|
{
|
|
struct scsc_mif_abs *mif;
|
|
unsigned long flags;
|
|
int which_bit = 0;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
/* Search for free slots */
|
|
which_bit = find_first_zero_bit(intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
|
|
if (which_bit >= MIFINTRBIT_NUM_INT)
|
|
goto error;
|
|
|
|
if (intr->mifintrbit_irq_handler[which_bit] != mifintrbit_default_handler) {
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
goto error;
|
|
}
|
|
|
|
/* Get abs implementation */
|
|
mif = intr->mif;
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
/* Mask to prevent spurious incoming interrupts */
|
|
mif->irq_bit_mask(mif, which_bit, intr->target);
|
|
/* Clear the interrupt */
|
|
mif->irq_bit_clear(mif, which_bit, intr->target);
|
|
|
|
/* Register the handler */
|
|
intr->mifintrbit_irq_handler[which_bit] = handler;
|
|
intr->irq_data[which_bit] = data;
|
|
|
|
/* Once registration is set, and IRQ has been cleared, unmask the interrupt */
|
|
mif->irq_bit_unmask(mif, which_bit, intr->target);
|
|
#else
|
|
/* Mask to prevent spurious incoming interrupts */
|
|
mif->irq_bit_mask(mif, which_bit);
|
|
/* Clear the interrupt */
|
|
mif->irq_bit_clear(mif, which_bit);
|
|
|
|
/* Register the handler */
|
|
intr->mifintrbit_irq_handler[which_bit] = handler;
|
|
intr->irq_data[which_bit] = data;
|
|
|
|
/* Once registration is set, and IRQ has been cleared, unmask the interrupt */
|
|
mif->irq_bit_unmask(mif, which_bit);
|
|
#endif
|
|
/* Update bit mask */
|
|
set_bit(which_bit, intr->bitmap_tohost);
|
|
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
|
|
return which_bit;
|
|
|
|
error:
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
SCSC_TAG_ERR(MIF, "Error registering irq\n");
|
|
return -EIO;
|
|
}
|
|
|
|
int mifintrbit_free_tohost(struct mifintrbit *intr, int which_bit)
|
|
{
|
|
struct scsc_mif_abs *mif;
|
|
unsigned long flags;
|
|
|
|
if (which_bit >= MIFINTRBIT_NUM_INT)
|
|
goto error;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
/* Get abs implementation */
|
|
mif = intr->mif;
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
/* Mask to prevent spurious incoming interrupts */
|
|
mif->irq_bit_mask(mif, which_bit, intr->target);
|
|
/* Set the handler with default */
|
|
intr->mifintrbit_irq_handler[which_bit] = mifintrbit_default_handler;
|
|
intr->irq_data[which_bit] = NULL;
|
|
/* Clear the interrupt for hygiene */
|
|
mif->irq_bit_clear(mif, which_bit, intr->target);
|
|
#else
|
|
/* Mask to prevent spurious incoming interrupts */
|
|
mif->irq_bit_mask(mif, which_bit);
|
|
/* Set the handler with default */
|
|
intr->mifintrbit_irq_handler[which_bit] = mifintrbit_default_handler;
|
|
intr->irq_data[which_bit] = NULL;
|
|
/* Clear the interrupt for hygiene */
|
|
mif->irq_bit_clear(mif, which_bit);
|
|
#endif
|
|
/* Update bit mask */
|
|
clear_bit(which_bit, intr->bitmap_tohost);
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
SCSC_TAG_ERR(MIF, "Error unregistering irq\n");
|
|
return -EIO;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
int mifintrbit_alloc_fromhost(struct mifintrbit *intr)
|
|
#else
|
|
int mifintrbit_alloc_fromhost(struct mifintrbit *intr, enum scsc_mif_abs_target target)
|
|
#endif
|
|
{
|
|
unsigned long flags;
|
|
int which_bit = 0;
|
|
unsigned long *p;
|
|
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
p = intr->bitmap_fromhost;
|
|
#else
|
|
if (target == SCSC_MIF_ABS_TARGET_WLAN)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_1)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_2)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
#else
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_1)
|
|
p = intr->bitmap_fromhost_fxm_1;
|
|
#endif
|
|
else
|
|
goto error;
|
|
#endif /* CONFIG_SCSC_INDEPENDENT_SUBSYSTEM */
|
|
|
|
/* Search for free slots */
|
|
which_bit = find_first_zero_bit(p, MIFINTRBIT_NUM_INT);
|
|
|
|
if (which_bit == MIFINTRBIT_NUM_INT)
|
|
goto error;
|
|
|
|
/* Update bit mask */
|
|
set_bit(which_bit, p);
|
|
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
|
|
return which_bit;
|
|
error:
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
SCSC_TAG_ERR(MIF, "Error allocating bit %d on %s\n",
|
|
which_bit, (intr->target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN");
|
|
#else
|
|
SCSC_TAG_ERR(MIF, "Error allocating bit %d on %s\n",
|
|
which_bit, (target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN");
|
|
#endif /* CONFIG_SCSC_INDEPENDENT_SUBSYSTEM */
|
|
return -EIO;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
int mifintrbit_free_fromhost(struct mifintrbit *intr, int which_bit)
|
|
#else
|
|
int mifintrbit_free_fromhost(struct mifintrbit *intr, int which_bit, enum scsc_mif_abs_target target)
|
|
#endif
|
|
{
|
|
unsigned long flags;
|
|
unsigned long *p;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
|
|
|
|
if (which_bit >= MIFINTRBIT_NUM_INT)
|
|
goto error;
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
p = intr->bitmap_fromhost;
|
|
#else
|
|
if (target == SCSC_MIF_ABS_TARGET_WLAN)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_1)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_2)
|
|
p = intr->bitmap_fromhost_wlan;
|
|
#else
|
|
else if (target == SCSC_MIF_ABS_TARGET_FXM_1)
|
|
p = intr->bitmap_fromhost_fxm_1;
|
|
#endif
|
|
else
|
|
goto error;
|
|
#endif /*CONFIG_SCSC_INDEPENDENT_SUBSYSTEM*/
|
|
|
|
/* Clear bit mask */
|
|
clear_bit(which_bit, p);
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
|
|
return 0;
|
|
error:
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
SCSC_TAG_ERR(MIF, "Error freeing bit %d on %s\n",
|
|
which_bit, (intr->target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN");
|
|
#else
|
|
SCSC_TAG_ERR(MIF, "Error freeing bit %d on %s\n",
|
|
which_bit, (target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN");
|
|
#endif /*CONFIG_SCSC_INDEPENDENT_SUBSYSTEM*/
|
|
return -EIO;
|
|
}
|
|
|
|
/* core API */
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
void mifintrbit_deinit(struct mifintrbit *intr, enum scsc_mif_abs_target target)
|
|
#else
|
|
void mifintrbit_deinit(struct mifintrbit *intr)
|
|
#endif
|
|
{
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
spin_lock_irqsave(&intr->spinlock, flags);
|
|
/* Set all handlers to default before unregistering the handler */
|
|
for (i = 0; i < MIFINTRBIT_NUM_INT; i++)
|
|
intr->mifintrbit_irq_handler[i] = mifintrbit_default_handler;
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
SCSC_TAG_INFO(MIF, "MIF IRQ deinit on %s\n", (target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN");
|
|
|
|
if (target == SCSC_MIF_ABS_TARGET_WLAN) {
|
|
SCSC_TAG_INFO(MIF, "MIF IRQ Unregistering IRQ handler WLAN\n");
|
|
intr->mif->irq_unreg_handler(intr->mif);
|
|
} else if (target == SCSC_MIF_ABS_TARGET_WPAN) {
|
|
SCSC_TAG_INFO(MIF, "MIF IRQ Unregistering IRQ handler WPAN\n");
|
|
intr->mif->irq_unreg_handler_wpan(intr->mif);
|
|
}
|
|
#else
|
|
intr->mif->irq_unreg_handler(intr->mif);
|
|
#endif
|
|
spin_unlock_irqrestore(&intr->spinlock, flags);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
void mifintrbit_init(struct mifintrbit *intr, struct scsc_mif_abs *mif, enum scsc_mif_abs_target target)
|
|
#else
|
|
void mifintrbit_init(struct mifintrbit *intr, struct scsc_mif_abs *mif)
|
|
#endif
|
|
{
|
|
int i;
|
|
#if IS_ENABLED(CONFIG_SCSC_PCIE_PAEAN_X86) || IS_ENABLED(CONFIG_SOC_S5E9925)
|
|
u8 start;
|
|
u8 end;
|
|
#endif
|
|
|
|
spin_lock_init(&intr->spinlock);
|
|
/* Set all handlers to default before hooking the hardware interrupt */
|
|
for (i = 0; i < MIFINTRBIT_NUM_INT; i++)
|
|
intr->mifintrbit_irq_handler[i] = mifintrbit_default_handler;
|
|
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
/* reset bitmaps */
|
|
/* store the target "type", we will use it to get the obj pointer */
|
|
intr->target = target;
|
|
bitmap_zero(intr->bitmap_fromhost, MIFINTRBIT_NUM_INT);
|
|
#if IS_ENABLED(CONFIG_SCSC_PCIE_PAEAN_X86) || IS_ENABLED(CONFIG_SOC_S5E9925)
|
|
mif->get_msi_range(mif, &start, &end, target);
|
|
/* fill bitmap */
|
|
bitmap_fill(intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
/* Clear specific range to map to actual phy MSI*/
|
|
bitmap_clear(intr->bitmap_tohost, start, (end - start) + 1);
|
|
#else
|
|
/* reset bitmaps */
|
|
bitmap_zero(intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
#endif
|
|
#else
|
|
/* reset bitmaps */
|
|
bitmap_zero(intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
|
|
bitmap_zero(intr->bitmap_fromhost_wlan, MIFINTRBIT_NUM_INT);
|
|
bitmap_zero(intr->bitmap_fromhost_fxm_1, MIFINTRBIT_NUM_INT);
|
|
#endif
|
|
|
|
/**
|
|
* Pre-allocate/reserve MIF interrupt bits 0 in both
|
|
* .._fromhost interrupt bits.
|
|
*
|
|
* These bits are used for purpose of forcing Panics from
|
|
* either MX manager or GDB monitor channels.
|
|
*/
|
|
#if IS_ENABLED(CONFIG_SCSC_INDEPENDENT_SUBSYSTEM)
|
|
if (target == SCSC_MIF_ABS_TARGET_WLAN) {
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_WLAN, intr->bitmap_fromhost);
|
|
SCSC_TAG_INFO(MIF, "MIF IRQ Registering IRQ handler WLAN\n");
|
|
/* register isr with mif abstraction */
|
|
mif->irq_reg_handler(mif, mifiintrman_isr, (void *)intr);
|
|
} else if (target == SCSC_MIF_ABS_TARGET_WPAN) {
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_WPAN, intr->bitmap_fromhost);
|
|
SCSC_TAG_INFO(MIF, "MIF IRQ Registering IRQ handler WPAN\n");
|
|
/* register isr with mif abstraction */
|
|
mif->irq_reg_handler_wpan(mif, mifiintrman_isr_wpan, (void *)intr);
|
|
}
|
|
#else
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_WLAN, intr->bitmap_fromhost_wlan);
|
|
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_FXM_1, intr->bitmap_fromhost_fxm_1);
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_FXM_2, intr->bitmap_fromhost_fxm_2);
|
|
#else
|
|
set_bit(MIFINTRBIT_RESERVED_PANIC_FXM_1, intr->bitmap_fromhost_fxm_1);
|
|
#endif
|
|
|
|
/* register isr with mif abstraction */
|
|
mif->irq_reg_handler(mif, mifiintrman_isr, (void *)intr);
|
|
#endif /* CONFIG_SCSC_INDEPENDENT_SUBSYSTEM */
|
|
|
|
/* cache mif */
|
|
intr->mif = mif;
|
|
}
|