MIPS: Octeon: Setup irq_domains for interrupts.
Create two domains. One for the GPIO lines, and the other for on-chip sources. Signed-off-by: David Daney <david.daney@cavium.com> Cc: linux-mips@linux-mips.org Cc: devicetree-discuss@lists.ozlabs.org Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Rob Herring <rob.herring@calxeda.com> Cc: linux-kernel@vger.kernel.org Cc: David Daney <david.daney@cavium.com> Patchwork: https://patchwork.linux-mips.org/patch/3936/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Родитель
7ed1815296
Коммит
a0c16582b5
|
@ -3,14 +3,17 @@
|
||||||
* License. See the file "COPYING" in the main directory of this archive
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
* for more details.
|
* for more details.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks
|
* Copyright (C) 2004-2012 Cavium, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include <asm/octeon/octeon.h>
|
#include <asm/octeon/octeon.h>
|
||||||
|
|
||||||
|
@ -42,9 +45,9 @@ struct octeon_core_chip_data {
|
||||||
|
|
||||||
static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
|
static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
|
||||||
|
|
||||||
static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit,
|
static void octeon_irq_set_ciu_mapping(int irq, int line, int bit,
|
||||||
struct irq_chip *chip,
|
struct irq_chip *chip,
|
||||||
irq_flow_handler_t handler)
|
irq_flow_handler_t handler)
|
||||||
{
|
{
|
||||||
union octeon_ciu_chip_data cd;
|
union octeon_ciu_chip_data cd;
|
||||||
|
|
||||||
|
@ -847,6 +850,178 @@ static struct irq_chip octeon_irq_chip_ciu_wd = {
|
||||||
.irq_mask = octeon_irq_dummy_mask,
|
.irq_mask = octeon_irq_dummy_mask,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit)
|
||||||
|
{
|
||||||
|
bool edge = false;
|
||||||
|
|
||||||
|
if (line == 0)
|
||||||
|
switch (bit) {
|
||||||
|
case 48 ... 49: /* GMX DRP */
|
||||||
|
case 50: /* IPD_DRP */
|
||||||
|
case 52 ... 55: /* Timers */
|
||||||
|
case 58: /* MPI */
|
||||||
|
edge = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else /* line == 1 */
|
||||||
|
switch (bit) {
|
||||||
|
case 47: /* PTP */
|
||||||
|
edge = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct octeon_irq_gpio_domain_data {
|
||||||
|
unsigned int base_hwirq;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int octeon_irq_gpio_xlat(struct irq_domain *d,
|
||||||
|
struct device_node *node,
|
||||||
|
const u32 *intspec,
|
||||||
|
unsigned int intsize,
|
||||||
|
unsigned long *out_hwirq,
|
||||||
|
unsigned int *out_type)
|
||||||
|
{
|
||||||
|
unsigned int type;
|
||||||
|
unsigned int pin;
|
||||||
|
unsigned int trigger;
|
||||||
|
struct octeon_irq_gpio_domain_data *gpiod;
|
||||||
|
|
||||||
|
if (d->of_node != node)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (intsize < 2)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pin = intspec[0];
|
||||||
|
if (pin >= 16)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
trigger = intspec[1];
|
||||||
|
|
||||||
|
switch (trigger) {
|
||||||
|
case 1:
|
||||||
|
type = IRQ_TYPE_EDGE_RISING;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
type = IRQ_TYPE_EDGE_FALLING;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
type = IRQ_TYPE_LEVEL_HIGH;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
type = IRQ_TYPE_LEVEL_LOW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
|
||||||
|
node->name,
|
||||||
|
trigger);
|
||||||
|
type = IRQ_TYPE_LEVEL_LOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*out_type = type;
|
||||||
|
gpiod = d->host_data;
|
||||||
|
*out_hwirq = gpiod->base_hwirq + pin;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int octeon_irq_ciu_xlat(struct irq_domain *d,
|
||||||
|
struct device_node *node,
|
||||||
|
const u32 *intspec,
|
||||||
|
unsigned int intsize,
|
||||||
|
unsigned long *out_hwirq,
|
||||||
|
unsigned int *out_type)
|
||||||
|
{
|
||||||
|
unsigned int ciu, bit;
|
||||||
|
|
||||||
|
ciu = intspec[0];
|
||||||
|
bit = intspec[1];
|
||||||
|
|
||||||
|
if (ciu > 1 || bit > 63)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* These are the GPIO lines */
|
||||||
|
if (ciu == 0 && bit >= 16 && bit < 32)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*out_hwirq = (ciu << 6) | bit;
|
||||||
|
*out_type = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip *octeon_irq_ciu_chip;
|
||||||
|
static struct irq_chip *octeon_irq_gpio_chip;
|
||||||
|
|
||||||
|
static bool octeon_irq_virq_in_range(unsigned int virq)
|
||||||
|
{
|
||||||
|
/* We cannot let it overflow the mapping array. */
|
||||||
|
if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0])))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
WARN_ONCE(true, "virq out of range %u.\n", virq);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int octeon_irq_ciu_map(struct irq_domain *d,
|
||||||
|
unsigned int virq, irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
unsigned int line = hw >> 6;
|
||||||
|
unsigned int bit = hw & 63;
|
||||||
|
|
||||||
|
if (!octeon_irq_virq_in_range(virq))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (octeon_irq_ciu_is_edge(line, bit))
|
||||||
|
octeon_irq_set_ciu_mapping(virq, line, bit,
|
||||||
|
octeon_irq_ciu_chip,
|
||||||
|
handle_edge_irq);
|
||||||
|
else
|
||||||
|
octeon_irq_set_ciu_mapping(virq, line, bit,
|
||||||
|
octeon_irq_ciu_chip,
|
||||||
|
handle_level_irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int octeon_irq_gpio_map(struct irq_domain *d,
|
||||||
|
unsigned int virq, irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
unsigned int line = hw >> 6;
|
||||||
|
unsigned int bit = hw & 63;
|
||||||
|
|
||||||
|
if (!octeon_irq_virq_in_range(virq))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
octeon_irq_set_ciu_mapping(virq, line, bit,
|
||||||
|
octeon_irq_gpio_chip,
|
||||||
|
octeon_irq_handle_gpio);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
|
||||||
|
.map = octeon_irq_ciu_map,
|
||||||
|
.xlate = octeon_irq_ciu_xlat,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
|
||||||
|
.map = octeon_irq_gpio_map,
|
||||||
|
.xlate = octeon_irq_gpio_xlat,
|
||||||
|
};
|
||||||
|
|
||||||
static void octeon_irq_ip2_v1(void)
|
static void octeon_irq_ip2_v1(void)
|
||||||
{
|
{
|
||||||
const unsigned long core_id = cvmx_get_core_num();
|
const unsigned long core_id = cvmx_get_core_num();
|
||||||
|
@ -972,7 +1147,8 @@ static void __init octeon_irq_init_ciu(void)
|
||||||
struct irq_chip *chip;
|
struct irq_chip *chip;
|
||||||
struct irq_chip *chip_mbox;
|
struct irq_chip *chip_mbox;
|
||||||
struct irq_chip *chip_wd;
|
struct irq_chip *chip_wd;
|
||||||
struct irq_chip *chip_gpio;
|
struct device_node *gpio_node;
|
||||||
|
struct device_node *ciu_node;
|
||||||
|
|
||||||
octeon_irq_init_ciu_percpu();
|
octeon_irq_init_ciu_percpu();
|
||||||
octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
|
octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
|
||||||
|
@ -986,15 +1162,16 @@ static void __init octeon_irq_init_ciu(void)
|
||||||
chip = &octeon_irq_chip_ciu_v2;
|
chip = &octeon_irq_chip_ciu_v2;
|
||||||
chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
|
chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
|
||||||
chip_wd = &octeon_irq_chip_ciu_wd_v2;
|
chip_wd = &octeon_irq_chip_ciu_wd_v2;
|
||||||
chip_gpio = &octeon_irq_chip_ciu_gpio_v2;
|
octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
|
||||||
} else {
|
} else {
|
||||||
octeon_irq_ip2 = octeon_irq_ip2_v1;
|
octeon_irq_ip2 = octeon_irq_ip2_v1;
|
||||||
octeon_irq_ip3 = octeon_irq_ip3_v1;
|
octeon_irq_ip3 = octeon_irq_ip3_v1;
|
||||||
chip = &octeon_irq_chip_ciu;
|
chip = &octeon_irq_chip_ciu;
|
||||||
chip_mbox = &octeon_irq_chip_ciu_mbox;
|
chip_mbox = &octeon_irq_chip_ciu_mbox;
|
||||||
chip_wd = &octeon_irq_chip_ciu_wd;
|
chip_wd = &octeon_irq_chip_ciu_wd;
|
||||||
chip_gpio = &octeon_irq_chip_ciu_gpio;
|
octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
|
||||||
}
|
}
|
||||||
|
octeon_irq_ciu_chip = chip;
|
||||||
octeon_irq_ip4 = octeon_irq_ip4_mask;
|
octeon_irq_ip4 = octeon_irq_ip4_mask;
|
||||||
|
|
||||||
/* Mips internal */
|
/* Mips internal */
|
||||||
|
@ -1003,8 +1180,6 @@ static void __init octeon_irq_init_ciu(void)
|
||||||
/* CIU_0 */
|
/* CIU_0 */
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);
|
octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip_gpio, octeon_irq_handle_gpio);
|
|
||||||
|
|
||||||
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
|
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
|
||||||
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
|
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
|
||||||
|
@ -1035,6 +1210,28 @@ static void __init octeon_irq_init_ciu(void)
|
||||||
octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);
|
octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);
|
||||||
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq);
|
octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq);
|
||||||
|
|
||||||
|
gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
|
||||||
|
if (gpio_node) {
|
||||||
|
struct octeon_irq_gpio_domain_data *gpiod;
|
||||||
|
|
||||||
|
gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
|
||||||
|
if (gpiod) {
|
||||||
|
/* gpio domain host_data is the base hwirq number. */
|
||||||
|
gpiod->base_hwirq = 16;
|
||||||
|
irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
|
||||||
|
of_node_put(gpio_node);
|
||||||
|
} else
|
||||||
|
pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
|
||||||
|
} else
|
||||||
|
pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
|
||||||
|
|
||||||
|
ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
|
||||||
|
if (ciu_node) {
|
||||||
|
irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
|
||||||
|
of_node_put(ciu_node);
|
||||||
|
} else
|
||||||
|
pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n");
|
||||||
|
|
||||||
/* Enable the CIU lines */
|
/* Enable the CIU lines */
|
||||||
set_c0_status(STATUSF_IP3 | STATUSF_IP2);
|
set_c0_status(STATUSF_IP3 | STATUSF_IP2);
|
||||||
clear_c0_status(STATUSF_IP4);
|
clear_c0_status(STATUSF_IP4);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче