From: David Daney <david.daney@xxxxxxxxxx> 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@xxxxxxxxxx> --- arch/mips/Kconfig | 1 + arch/mips/cavium-octeon/octeon-irq.c | 279 +++++++++++++++++++++++++++++++++- 2 files changed, 279 insertions(+), 1 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index d46f1da..97c2cf2 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1404,6 +1404,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..5e08590 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,178 @@ 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 (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: + WARN(1, "hello"); + 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 int octeon_irq_ciu_each_hwirq(struct irq_domain *d, + int (*cb)(struct irq_domain *d, + 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, 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, + .each_hwirq = octeon_irq_ciu_each_hwirq, + .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 +681,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 +959,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 +1157,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 +1172,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 +1180,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 +1191,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 +1265,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