lkml.org 
[lkml]   [2011]   [Dec]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 4/4] MIPS: Octeon: Add irq_create_of_mapping() and GPIO interrupts.
    Date
    From: David Daney <david.daney@cavium.com>

    This is needed for Octeon to use the Device Tree.

    The GPIO interrupts are configured based on Device Tree properties

    Signed-off-by: David Daney <david.daney@cavium.com>
    ---
    arch/mips/Kconfig | 1 +
    arch/mips/cavium-octeon/octeon-irq.c | 259 +++++++++++++++++++++++++++++++++-
    2 files changed, 259 insertions(+), 1 deletions(-)

    diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
    index 825ded9..6685497 100644
    --- a/arch/mips/Kconfig
    +++ b/arch/mips/Kconfig
    @@ -1432,6 +1432,7 @@ config CPU_CAVIUM_OCTEON
    select WEAK_ORDERING
    select CPU_SUPPORTS_HIGHMEM
    select CPU_SUPPORTS_HUGEPAGES
    + select IRQ_DOMAIN
    help
    The Cavium Octeon processor is a highly integrated chip containing
    many ethernet hardware widgets for networking tasks. The processor
    diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
    index ffd4ae6..9a9d4a7 100644
    --- a/arch/mips/cavium-octeon/octeon-irq.c
    +++ b/arch/mips/cavium-octeon/octeon-irq.c
    @@ -7,12 +7,16 @@
    */

    #include <linux/interrupt.h>
    +#include <linux/irqdomain.h>
    #include <linux/bitops.h>
    +#include <linux/module.h>
    #include <linux/percpu.h>
    +#include <linux/of_irq.h>
    #include <linux/irq.h>
    #include <linux/smp.h>

    #include <asm/octeon/octeon.h>
    +#include <asm/octeon/cvmx-gpio-defs.h>

    static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock);
    static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock);
    @@ -58,6 +62,158 @@ static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit,
    octeon_irq_ciu_to_irq[line][bit] = irq;
    }

    +static int octeon_irq_gpio_dt_translate(struct irq_domain *d,
    + struct device_node *node,
    + const u32 *intspec,
    + unsigned int intsize,
    + unsigned int *out_hwirq,
    + unsigned int *out_type)
    +{
    + unsigned int irq;
    + unsigned int type;
    + unsigned int ciu = 0, bit = 0;
    + unsigned int pin;
    + unsigned int trigger;
    + bool set_edge_handler = false;
    +
    + if (d->of_node != node)
    + return -EINVAL;
    +
    + if (intsize < 2)
    + return -EINVAL;
    +
    + pin = be32_to_cpup(intspec);
    + if (pin >= 16)
    + return -EINVAL;
    +
    + trigger = be32_to_cpup(intspec + 1);
    +
    + switch (trigger) {
    + case 1:
    + type = IRQ_TYPE_EDGE_RISING;
    + set_edge_handler = true;
    + break;
    + case 2:
    + type = IRQ_TYPE_EDGE_FALLING;
    + set_edge_handler = true;
    + 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;
    + *out_hwirq = d->hwirq_base + pin;
    +
    + ciu = *out_hwirq >> 6;
    + bit = *out_hwirq & 0x3f;
    +
    + irq = octeon_irq_ciu_to_irq[ciu][bit];
    +
    + if (set_edge_handler)
    + __irq_set_handler(irq, handle_edge_irq, 0, NULL);
    +
    +
    + return 0;
    +}
    +
    +/*
    + * octeon_irq_ciu_dt_translate - Hook to resolve OF irq specifier into a Linux irq#
    + *
    + * Octeon irq maps are a pair of indexes. The first selects either
    + * ciu0 or ciu1, the second is the bit within the ciu register.
    + */
    +static int octeon_irq_ciu_dt_translate(struct irq_domain *d,
    + struct device_node *node,
    + const u32 *intspec,
    + unsigned int intsize,
    + unsigned int *out_hwirq,
    + unsigned int *out_type)
    +{
    + unsigned int ciu, bit;
    +
    + ciu = be32_to_cpup(intspec);
    + bit = be32_to_cpup(intspec + 1);
    +
    + if (ciu > 1 || bit > 63)
    + return -EINVAL;
    +
    + if (octeon_irq_ciu_to_irq[ciu][bit] == 0)
    + return -EINVAL;
    +
    + *out_hwirq = (ciu << 6) | bit;
    + *out_type = 0;
    +
    + return 0;
    +}
    +
    +static unsigned int octeon_irq_ciu_to_irqf(struct irq_domain *d,
    + unsigned int hwirq)
    +{
    + unsigned int ciu, bit;
    +
    + ciu = (hwirq >> 6) & 1;
    + bit = hwirq & 0x3f;
    + return octeon_irq_ciu_to_irq[ciu][bit];
    +}
    +
    +static bool octeon_irq_ciu_in_domain(unsigned int irq)
    +{
    + return (irq != 0) &&
    + !(irq >= OCTEON_IRQ_GPIO0 && irq < OCTEON_IRQ_GPIO0 + 16);
    +}
    +
    +static int octeon_irq_ciu_each_irq(struct irq_domain *d,
    + int (*cb)(struct irq_domain *d,
    + unsigned int irq,
    + unsigned int hwirq))
    +{
    + int ciu, bit;
    + int ret = 0;
    + unsigned int irq, hwirq;
    +
    + for (ciu = 0; ciu <= 1; ciu++)
    + for (bit = 0; bit <= 63; bit++) {
    + irq = octeon_irq_ciu_to_irq[ciu][bit];
    + hwirq = (ciu << 6) | bit;
    + if (octeon_irq_ciu_in_domain(irq)) {
    + ret = cb(d, irq, hwirq);
    + if (ret)
    + return ret;
    + }
    + }
    + return 0;
    +}
    +
    +static const struct irq_domain_ops octeon_irq_ciu_domain_ops = {
    + .to_irq = octeon_irq_ciu_to_irqf,
    + .each_irq = octeon_irq_ciu_each_irq,
    + .dt_translate = octeon_irq_ciu_dt_translate,
    +};
    +
    +static const struct irq_domain_ops octeon_irq_gpio_domain_ops = {
    + .to_irq = octeon_irq_ciu_to_irqf,
    + .dt_translate = octeon_irq_gpio_dt_translate,
    +};
    +
    +static struct irq_domain octeon_irq_ciu_domain = {
    + .ops = &octeon_irq_ciu_domain_ops
    +};
    +
    +static struct irq_domain octeon_irq_gpio_domain = {
    + .irq_base = OCTEON_IRQ_GPIO0,
    + .nr_irq = 16,
    + .ops = &octeon_irq_gpio_domain_ops
    +};
    +
    static int octeon_coreid_for_cpu(int cpu)
    {
    #ifdef CONFIG_SMP
    @@ -505,6 +661,72 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
    }
    }

    +static void octeon_irq_gpio_setup(struct irq_data *data)
    +{
    + union cvmx_gpio_bit_cfgx cfg;
    + int bit = data->irq - OCTEON_IRQ_GPIO0;
    + u32 t = irqd_get_trigger_type(data);
    +
    + cfg.u64 = 0;
    + cfg.s.int_en = 1;
    + cfg.s.int_type = (t & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) != 0;
    + cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
    +
    + /* 1 uS glitch filter*/
    + cfg.s.fil_cnt = 7;
    + cfg.s.fil_sel = 3;
    +
    + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), cfg.u64);
    +}
    +
    +static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
    +{
    + octeon_irq_gpio_setup(data);
    + octeon_irq_ciu_enable_v2(data);
    +}
    +
    +static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
    +{
    + octeon_irq_gpio_setup(data);
    + octeon_irq_ciu_enable(data);
    +}
    +
    +static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
    +{
    + u32 current_type = irqd_get_trigger_type(data);
    +
    + /* If the type has been set, don't change it */
    + if (current_type && current_type != t)
    + return -EINVAL;
    +
    + irqd_set_trigger_type(data, t);
    + return IRQ_SET_MASK_OK;
    +}
    +
    +static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
    +{
    + int bit = data->irq - OCTEON_IRQ_GPIO0;
    + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), 0);
    +
    + octeon_irq_ciu_disable_all_v2(data);
    +}
    +
    +static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
    +{
    + int bit = data->irq - OCTEON_IRQ_GPIO0;
    + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), 0);
    +
    + octeon_irq_ciu_disable_all(data);
    +}
    +
    +static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
    +{
    + int bit = data->irq - OCTEON_IRQ_GPIO0;
    + u64 mask = 1ull << bit;
    +
    + cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
    +}
    +
    #ifdef CONFIG_SMP

    static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
    @@ -717,6 +939,31 @@ static struct irq_chip octeon_irq_chip_ciu_mbox = {
    .flags = IRQCHIP_ONOFFLINE_ENABLED,
    };

    +static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
    + .name = "CIU-GPIO",
    + .irq_enable = octeon_irq_ciu_enable_gpio_v2,
    + .irq_disable = octeon_irq_ciu_disable_gpio_v2,
    + .irq_ack = octeon_irq_ciu_gpio_ack,
    + .irq_mask = octeon_irq_ciu_disable_local_v2,
    + .irq_unmask = octeon_irq_ciu_enable_v2,
    + .irq_set_type = octeon_irq_ciu_gpio_set_type,
    +#ifdef CONFIG_SMP
    + .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
    +#endif
    +};
    +
    +static struct irq_chip octeon_irq_chip_ciu_gpio = {
    + .name = "CIU-GPIO",
    + .irq_enable = octeon_irq_ciu_enable_gpio,
    + .irq_disable = octeon_irq_ciu_disable_gpio,
    + .irq_mask = octeon_irq_dummy_mask,
    + .irq_ack = octeon_irq_ciu_gpio_ack,
    + .irq_set_type = octeon_irq_ciu_gpio_set_type,
    +#ifdef CONFIG_SMP
    + .irq_set_affinity = octeon_irq_ciu_set_affinity,
    +#endif
    +};
    +
    /*
    * Watchdog interrupts are special. They are associated with a single
    * core, so we hardwire the affinity to that core.
    @@ -890,6 +1137,7 @@ static void __init octeon_irq_init_ciu(void)
    struct irq_chip *chip_edge;
    struct irq_chip *chip_mbox;
    struct irq_chip *chip_wd;
    + struct irq_chip *chip_gpio;

    octeon_irq_init_ciu_percpu();
    octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
    @@ -904,6 +1152,7 @@ static void __init octeon_irq_init_ciu(void)
    chip_edge = &octeon_irq_chip_ciu_edge_v2;
    chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
    chip_wd = &octeon_irq_chip_ciu_wd_v2;
    + chip_gpio = &octeon_irq_chip_ciu_gpio_v2;
    } else {
    octeon_irq_ip2 = octeon_irq_ip2_v1;
    octeon_irq_ip3 = octeon_irq_ip3_v1;
    @@ -911,6 +1160,7 @@ static void __init octeon_irq_init_ciu(void)
    chip_edge = &octeon_irq_chip_ciu_edge;
    chip_mbox = &octeon_irq_chip_ciu_mbox;
    chip_wd = &octeon_irq_chip_ciu_wd;
    + chip_gpio = &octeon_irq_chip_ciu_gpio;
    }
    octeon_irq_ip4 = octeon_irq_ip4_mask;

    @@ -921,7 +1171,7 @@ static void __init octeon_irq_init_ciu(void)
    for (i = 0; i < 16; i++)
    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, handle_level_irq);
    + octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip_gpio, handle_level_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);
    @@ -995,6 +1245,13 @@ static void __init octeon_irq_init_ciu(void)
    octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq);
    octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq);

    + octeon_irq_ciu_domain.of_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
    + irq_domain_add(&octeon_irq_ciu_domain);
    +
    + octeon_irq_gpio_domain.of_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
    + octeon_irq_gpio_domain.hwirq_base = ((0 << 6) | 16);
    + irq_domain_add(&octeon_irq_gpio_domain);
    +
    /* Enable the CIU lines */
    set_c0_status(STATUSF_IP3 | STATUSF_IP2);
    clear_c0_status(STATUSF_IP4);
    --
    1.7.2.3


    \
     
     \ /
      Last update: 2011-12-15 03:35    [W:0.055 / U:0.452 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site