powerpc/5200: Refactor mpc5200 interrupt controller driver
Rework the mpc5200-pic driver to simplify it and fix up the setting of desc->status when set_type is called for internal IRQs (so they are reported as level, not edge). The simplification is due to splitting off the handling of external IRQs into a separate block so they don't need to be handled as exceptions in the normal CRIT, MAIN and PERP paths. Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
Родитель
bc4346fe27
Коммит
8f2558ded5
|
@ -190,10 +190,10 @@ static void mpc52xx_extirq_ack(unsigned int virq)
|
|||
|
||||
static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type)
|
||||
{
|
||||
struct irq_desc *desc = get_irq_desc(virq);
|
||||
u32 ctrl_reg, type;
|
||||
int irq;
|
||||
int l2irq;
|
||||
void *handler = handle_level_irq;
|
||||
|
||||
irq = irq_map[virq].hwirq;
|
||||
l2irq = irq & MPC52xx_IRQ_L2_MASK;
|
||||
|
@ -201,32 +201,21 @@ static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type)
|
|||
pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type);
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
type = 0;
|
||||
break;
|
||||
case IRQF_TRIGGER_RISING:
|
||||
type = 1;
|
||||
break;
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
type = 2;
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
type = 3;
|
||||
break;
|
||||
case IRQF_TRIGGER_HIGH: type = 0; break;
|
||||
case IRQF_TRIGGER_RISING: type = 1; handler = handle_edge_irq; break;
|
||||
case IRQF_TRIGGER_FALLING: type = 2; handler = handle_edge_irq; break;
|
||||
case IRQF_TRIGGER_LOW: type = 3; break;
|
||||
default:
|
||||
type = 0;
|
||||
}
|
||||
|
||||
desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
|
||||
desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
|
||||
if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
|
||||
desc->status |= IRQ_LEVEL;
|
||||
|
||||
ctrl_reg = in_be32(&intr->ctrl);
|
||||
ctrl_reg &= ~(0x3 << (22 - (l2irq * 2)));
|
||||
ctrl_reg |= (type << (22 - (l2irq * 2)));
|
||||
out_be32(&intr->ctrl, ctrl_reg);
|
||||
|
||||
__set_irq_handler_unlocked(virq, handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,6 +230,11 @@ static struct irq_chip mpc52xx_extirq_irqchip = {
|
|||
/*
|
||||
* Main interrupt irq_chip
|
||||
*/
|
||||
static int mpc52xx_null_set_type(unsigned int virq, unsigned int flow_type)
|
||||
{
|
||||
return 0; /* Do nothing so that the sense mask will get updated */
|
||||
}
|
||||
|
||||
static void mpc52xx_main_mask(unsigned int virq)
|
||||
{
|
||||
int irq;
|
||||
|
@ -268,6 +262,7 @@ static struct irq_chip mpc52xx_main_irqchip = {
|
|||
.mask = mpc52xx_main_mask,
|
||||
.mask_ack = mpc52xx_main_mask,
|
||||
.unmask = mpc52xx_main_unmask,
|
||||
.set_type = mpc52xx_null_set_type,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -300,6 +295,7 @@ static struct irq_chip mpc52xx_periph_irqchip = {
|
|||
.mask = mpc52xx_periph_mask,
|
||||
.mask_ack = mpc52xx_periph_mask,
|
||||
.unmask = mpc52xx_periph_unmask,
|
||||
.set_type = mpc52xx_null_set_type,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -343,8 +339,18 @@ static struct irq_chip mpc52xx_sdma_irqchip = {
|
|||
.mask = mpc52xx_sdma_mask,
|
||||
.unmask = mpc52xx_sdma_unmask,
|
||||
.ack = mpc52xx_sdma_ack,
|
||||
.set_type = mpc52xx_null_set_type,
|
||||
};
|
||||
|
||||
/**
|
||||
* mpc52xx_is_extirq - Returns true if hwirq number is for an external IRQ
|
||||
*/
|
||||
static int mpc52xx_is_extirq(int l1, int l2)
|
||||
{
|
||||
return ((l1 == 0) && (l2 == 0)) ||
|
||||
((l1 == 1) && (l2 >= 1) && (l2 <= 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property
|
||||
*/
|
||||
|
@ -363,37 +369,22 @@ static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
|
|||
|
||||
intrvect_l1 = (int)intspec[0];
|
||||
intrvect_l2 = (int)intspec[1];
|
||||
intrvect_type = (int)intspec[2];
|
||||
intrvect_type = (int)intspec[2] & 0x3;
|
||||
|
||||
intrvect_linux = (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) &
|
||||
MPC52xx_IRQ_L1_MASK;
|
||||
intrvect_linux |= intrvect_l2 & MPC52xx_IRQ_L2_MASK;
|
||||
|
||||
*out_hwirq = intrvect_linux;
|
||||
*out_flags = IRQ_TYPE_LEVEL_LOW;
|
||||
if (mpc52xx_is_extirq(intrvect_l1, intrvect_l2))
|
||||
*out_flags = mpc52xx_map_senses[intrvect_type];
|
||||
|
||||
pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1,
|
||||
intrvect_l2);
|
||||
|
||||
*out_hwirq = intrvect_linux;
|
||||
*out_flags = mpc52xx_map_senses[intrvect_type];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mpc52xx_irqx_gettype - determine the IRQ sense type (level/edge)
|
||||
*
|
||||
* Only external IRQs need this.
|
||||
*/
|
||||
static int mpc52xx_irqx_gettype(int irq)
|
||||
{
|
||||
int type;
|
||||
u32 ctrl_reg;
|
||||
|
||||
ctrl_reg = in_be32(&intr->ctrl);
|
||||
type = (ctrl_reg >> (22 - irq * 2)) & 0x3;
|
||||
|
||||
return mpc52xx_map_senses[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure
|
||||
*/
|
||||
|
@ -402,68 +393,46 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
|
|||
{
|
||||
int l1irq;
|
||||
int l2irq;
|
||||
struct irq_chip *good_irqchip;
|
||||
void *good_handle;
|
||||
struct irq_chip *irqchip;
|
||||
void *hndlr;
|
||||
int type;
|
||||
u32 reg;
|
||||
|
||||
l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET;
|
||||
l2irq = irq & MPC52xx_IRQ_L2_MASK;
|
||||
|
||||
/*
|
||||
* Most of ours IRQs will be level low
|
||||
* Only external IRQs on some platform may be others
|
||||
* External IRQs are handled differently by the hardware so they are
|
||||
* handled by a dedicated irq_chip structure.
|
||||
*/
|
||||
type = IRQ_TYPE_LEVEL_LOW;
|
||||
if (mpc52xx_is_extirq(l1irq, l2irq)) {
|
||||
reg = in_be32(&intr->ctrl);
|
||||
type = mpc52xx_map_senses[(reg >> (22 - l2irq * 2)) & 0x3];
|
||||
if ((type == IRQ_TYPE_EDGE_FALLING) ||
|
||||
(type == IRQ_TYPE_EDGE_RISING))
|
||||
hndlr = handle_edge_irq;
|
||||
else
|
||||
hndlr = handle_level_irq;
|
||||
|
||||
set_irq_chip_and_handler(virq, &mpc52xx_extirq_irqchip, hndlr);
|
||||
pr_debug("%s: External IRQ%i virq=%x, hw=%x. type=%x\n",
|
||||
__func__, l2irq, virq, (int)irq, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* It is an internal SOC irq. Choose the correct irq_chip */
|
||||
switch (l1irq) {
|
||||
case MPC52xx_IRQ_L1_CRIT:
|
||||
pr_debug("%s: Critical. l2=%x\n", __func__, l2irq);
|
||||
|
||||
BUG_ON(l2irq != 0);
|
||||
|
||||
type = mpc52xx_irqx_gettype(l2irq);
|
||||
good_irqchip = &mpc52xx_extirq_irqchip;
|
||||
break;
|
||||
|
||||
case MPC52xx_IRQ_L1_MAIN:
|
||||
pr_debug("%s: Main IRQ[1-3] l2=%x\n", __func__, l2irq);
|
||||
|
||||
if ((l2irq >= 1) && (l2irq <= 3)) {
|
||||
type = mpc52xx_irqx_gettype(l2irq);
|
||||
good_irqchip = &mpc52xx_extirq_irqchip;
|
||||
} else {
|
||||
good_irqchip = &mpc52xx_main_irqchip;
|
||||
}
|
||||
break;
|
||||
|
||||
case MPC52xx_IRQ_L1_PERP:
|
||||
pr_debug("%s: Peripherals. l2=%x\n", __func__, l2irq);
|
||||
good_irqchip = &mpc52xx_periph_irqchip;
|
||||
break;
|
||||
|
||||
case MPC52xx_IRQ_L1_SDMA:
|
||||
pr_debug("%s: SDMA. l2=%x\n", __func__, l2irq);
|
||||
good_irqchip = &mpc52xx_sdma_irqchip;
|
||||
break;
|
||||
|
||||
case MPC52xx_IRQ_L1_MAIN: irqchip = &mpc52xx_main_irqchip; break;
|
||||
case MPC52xx_IRQ_L1_PERP: irqchip = &mpc52xx_periph_irqchip; break;
|
||||
case MPC52xx_IRQ_L1_SDMA: irqchip = &mpc52xx_sdma_irqchip; break;
|
||||
default:
|
||||
pr_err("%s: invalid virq requested (0x%x)\n", __func__, virq);
|
||||
pr_err("%s: invalid irq: virq=%i, l1=%i, l2=%i\n",
|
||||
__func__, virq, l1irq, l2irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
good_handle = handle_edge_irq;
|
||||
break;
|
||||
default:
|
||||
good_handle = handle_level_irq;
|
||||
}
|
||||
|
||||
set_irq_chip_and_handler(virq, good_irqchip, good_handle);
|
||||
|
||||
pr_debug("%s: virq=%x, hw=%x. type=%x\n", __func__, virq,
|
||||
(int)irq, type);
|
||||
set_irq_chip_and_handler(virq, irqchip, handle_level_irq);
|
||||
pr_debug("%s: virq=%x, l1=%i, l2=%i\n", __func__, virq, l1irq, l2irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -502,6 +471,8 @@ void __init mpc52xx_init_irq(void)
|
|||
panic(__FILE__ ": find_and_map failed on 'mpc5200-bestcomm'. "
|
||||
"Check node !");
|
||||
|
||||
pr_debug("MPC5200 IRQ controller mapped to 0x%p\n", intr);
|
||||
|
||||
/* Disable all interrupt sources. */
|
||||
out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */
|
||||
out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */
|
||||
|
|
Загрузка…
Ссылка в новой задаче