lineage_kernel_xcoverpro/drivers/misc/samsung/scsc/mifintrbit.c

264 lines
7.0 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
/* Uses */
#include <linux/bitmap.h>
#include <linux/errno.h>
#include <linux/spinlock.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;
unsigned long flags;
spin_lock_irqsave(&intr->spinlock, flags);
intr->mif->irq_bit_clear(intr->mif, irq);
spin_unlock_irqrestore(&intr->spinlock, flags);
}
static void print_bitmaps(struct mifintrbit *intr)
{
unsigned long dst1, dst2, dst3;
bitmap_copy_le(&dst1, intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
bitmap_copy_le(&dst2, intr->bitmap_fromhost_r4, MIFINTRBIT_NUM_INT);
bitmap_copy_le(&dst3, intr->bitmap_fromhost_m4, MIFINTRBIT_NUM_INT);
}
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);
irq_reg = intr->mif->irq_get(intr->mif);
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]);
}
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;
/* 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);
/* 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;
/* 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);
/* 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;
}
int mifintrbit_alloc_fromhost(struct mifintrbit *intr, enum scsc_mif_abs_target target)
{
unsigned long flags;
int which_bit = 0;
unsigned long *p;
spin_lock_irqsave(&intr->spinlock, flags);
if (target == SCSC_MIF_ABS_TARGET_R4)
p = intr->bitmap_fromhost_r4;
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
else if (target == SCSC_MIF_ABS_TARGET_M4)
p = intr->bitmap_fromhost_r4;
else if (target == SCSC_MIF_ABS_TARGET_M4_1)
p = intr->bitmap_fromhost_r4;
#else
else if (target == SCSC_MIF_ABS_TARGET_M4)
p = intr->bitmap_fromhost_m4;
#endif
else
goto error;
/* 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);
SCSC_TAG_ERR(MIF, "Error allocating bit %d on %s\n",
which_bit, target ? "M4" : "R4");
return -EIO;
}
int mifintrbit_free_fromhost(struct mifintrbit *intr, int which_bit, enum scsc_mif_abs_target target)
{
unsigned long flags;
unsigned long *p;
spin_lock_irqsave(&intr->spinlock, flags);
if (which_bit >= MIFINTRBIT_NUM_INT)
goto error;
if (target == SCSC_MIF_ABS_TARGET_R4)
p = intr->bitmap_fromhost_r4;
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
else if (target == SCSC_MIF_ABS_TARGET_M4)
p = intr->bitmap_fromhost_r4;
else if (target == SCSC_MIF_ABS_TARGET_M4_1)
p = intr->bitmap_fromhost_r4;
#else
else if (target == SCSC_MIF_ABS_TARGET_M4)
p = intr->bitmap_fromhost_m4;
#endif
else
goto error;
/* Clear bit mask */
clear_bit(which_bit, p);
spin_unlock_irqrestore(&intr->spinlock, flags);
return 0;
error:
spin_unlock_irqrestore(&intr->spinlock, flags);
SCSC_TAG_ERR(MIF, "Error freeing bit %d on %s\n",
which_bit, target ? "M4" : "R4");
return -EIO;
}
/* core API */
void mifintrbit_deinit(struct mifintrbit *intr)
{
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;
intr->mif->irq_unreg_handler(intr->mif);
spin_unlock_irqrestore(&intr->spinlock, flags);
}
void mifintrbit_init(struct mifintrbit *intr, struct scsc_mif_abs *mif)
{
int i;
spin_lock_init(&intr->spinlock);
/* Set all handlersd to default before hooking the hardware interrupt */
for (i = 0; i < MIFINTRBIT_NUM_INT; i++)
intr->mifintrbit_irq_handler[i] = mifintrbit_default_handler;
/* reset bitmaps */
bitmap_zero(intr->bitmap_tohost, MIFINTRBIT_NUM_INT);
bitmap_zero(intr->bitmap_fromhost_r4, MIFINTRBIT_NUM_INT);
bitmap_zero(intr->bitmap_fromhost_m4, MIFINTRBIT_NUM_INT);
/**
* Pre-allocate/reserve MIF interrupt bits 0 in both
* .._fromhost_r4 and .._fromhost_m4 interrupt bits.
*
* These bits are used for purpose of forcing Panics from
* either MX manager or GDB monitor channels.
*/
set_bit(MIFINTRBIT_RESERVED_PANIC_R4, intr->bitmap_fromhost_r4);
#ifdef CONFIG_SCSC_MX450_GDB_SUPPORT
set_bit(MIFINTRBIT_RESERVED_PANIC_M4, intr->bitmap_fromhost_m4);
set_bit(MIFINTRBIT_RESERVED_PANIC_M4_1, intr->bitmap_fromhost_m4_1);
#else
set_bit(MIFINTRBIT_RESERVED_PANIC_M4, intr->bitmap_fromhost_m4);
#endif
/* register isr with mif abstraction */
mif->irq_reg_handler(mif, mifiintrman_isr, (void *)intr);
/* cache mif */
intr->mif = mif;
}