162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Xtensa MX interrupt distributor 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2002 - 2013 Tensilica, Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 862306a36Sopenharmony_ci * for more details. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irqdomain.h> 1362306a36Sopenharmony_ci#include <linux/irq.h> 1462306a36Sopenharmony_ci#include <linux/irqchip.h> 1562306a36Sopenharmony_ci#include <linux/irqchip/xtensa-mx.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/mxregs.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define HW_IRQ_IPI_COUNT 2 2162306a36Sopenharmony_ci#define HW_IRQ_MX_BASE 2 2262306a36Sopenharmony_ci#define HW_IRQ_EXTERN_BASE 3 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, cached_irq_mask); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int xtensa_mx_irq_map(struct irq_domain *d, unsigned int irq, 2762306a36Sopenharmony_ci irq_hw_number_t hw) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci if (hw < HW_IRQ_IPI_COUNT) { 3062306a36Sopenharmony_ci struct irq_chip *irq_chip = d->host_data; 3162306a36Sopenharmony_ci irq_set_chip_and_handler_name(irq, irq_chip, 3262306a36Sopenharmony_ci handle_percpu_irq, "ipi"); 3362306a36Sopenharmony_ci irq_set_status_flags(irq, IRQ_LEVEL); 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq))); 3762306a36Sopenharmony_ci return xtensa_irq_map(d, irq, hw); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Device Tree IRQ specifier translation function which works with one or 4262306a36Sopenharmony_ci * two cell bindings. First cell value maps directly to the hwirq number. 4362306a36Sopenharmony_ci * Second cell if present specifies whether hwirq number is external (1) or 4462306a36Sopenharmony_ci * internal (0). 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int xtensa_mx_irq_domain_xlate(struct irq_domain *d, 4762306a36Sopenharmony_ci struct device_node *ctrlr, 4862306a36Sopenharmony_ci const u32 *intspec, unsigned int intsize, 4962306a36Sopenharmony_ci unsigned long *out_hwirq, unsigned int *out_type) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return xtensa_irq_domain_xlate(intspec, intsize, 5262306a36Sopenharmony_ci intspec[0], intspec[0] + HW_IRQ_EXTERN_BASE, 5362306a36Sopenharmony_ci out_hwirq, out_type); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct irq_domain_ops xtensa_mx_irq_domain_ops = { 5762306a36Sopenharmony_ci .xlate = xtensa_mx_irq_domain_xlate, 5862306a36Sopenharmony_ci .map = xtensa_mx_irq_map, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid secondary_init_irq(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci __this_cpu_write(cached_irq_mask, 6462306a36Sopenharmony_ci XCHAL_INTTYPE_MASK_EXTERN_EDGE | 6562306a36Sopenharmony_ci XCHAL_INTTYPE_MASK_EXTERN_LEVEL); 6662306a36Sopenharmony_ci xtensa_set_sr(XCHAL_INTTYPE_MASK_EXTERN_EDGE | 6762306a36Sopenharmony_ci XCHAL_INTTYPE_MASK_EXTERN_LEVEL, intenable); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void xtensa_mx_irq_mask(struct irq_data *d) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned int mask = 1u << d->hwirq; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | 7562306a36Sopenharmony_ci XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { 7662306a36Sopenharmony_ci unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (ext_irq >= HW_IRQ_MX_BASE) { 7962306a36Sopenharmony_ci set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENG); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci mask = __this_cpu_read(cached_irq_mask) & ~mask; 8462306a36Sopenharmony_ci __this_cpu_write(cached_irq_mask, mask); 8562306a36Sopenharmony_ci xtensa_set_sr(mask, intenable); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void xtensa_mx_irq_unmask(struct irq_data *d) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci unsigned int mask = 1u << d->hwirq; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | 9362306a36Sopenharmony_ci XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { 9462306a36Sopenharmony_ci unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ext_irq >= HW_IRQ_MX_BASE) { 9762306a36Sopenharmony_ci set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENGSET); 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci mask |= __this_cpu_read(cached_irq_mask); 10262306a36Sopenharmony_ci __this_cpu_write(cached_irq_mask, mask); 10362306a36Sopenharmony_ci xtensa_set_sr(mask, intenable); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void xtensa_mx_irq_enable(struct irq_data *d) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci xtensa_mx_irq_unmask(d); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void xtensa_mx_irq_disable(struct irq_data *d) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci xtensa_mx_irq_mask(d); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void xtensa_mx_irq_ack(struct irq_data *d) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci xtensa_set_sr(1 << d->hwirq, intclear); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int xtensa_mx_irq_retrigger(struct irq_data *d) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci unsigned int mask = 1u << d->hwirq; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE)) 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci xtensa_set_sr(mask, intset); 12862306a36Sopenharmony_ci return 1; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int xtensa_mx_irq_set_affinity(struct irq_data *d, 13262306a36Sopenharmony_ci const struct cpumask *dest, bool force) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int cpu = cpumask_any_and(dest, cpu_online_mask); 13562306a36Sopenharmony_ci unsigned mask = 1u << cpu; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE)); 13862306a36Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct irq_chip xtensa_mx_irq_chip = { 14562306a36Sopenharmony_ci .name = "xtensa-mx", 14662306a36Sopenharmony_ci .irq_enable = xtensa_mx_irq_enable, 14762306a36Sopenharmony_ci .irq_disable = xtensa_mx_irq_disable, 14862306a36Sopenharmony_ci .irq_mask = xtensa_mx_irq_mask, 14962306a36Sopenharmony_ci .irq_unmask = xtensa_mx_irq_unmask, 15062306a36Sopenharmony_ci .irq_ack = xtensa_mx_irq_ack, 15162306a36Sopenharmony_ci .irq_retrigger = xtensa_mx_irq_retrigger, 15262306a36Sopenharmony_ci .irq_set_affinity = xtensa_mx_irq_set_affinity, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void __init xtensa_mx_init_common(struct irq_domain *root_domain) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci irq_set_default_host(root_domain); 16062306a36Sopenharmony_ci secondary_init_irq(); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Initialize default IRQ routing to CPU 0 */ 16362306a36Sopenharmony_ci for (i = 0; i < XCHAL_NUM_EXTINTERRUPTS; ++i) 16462306a36Sopenharmony_ci set_er(1, MIROUT(i)); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint __init xtensa_mx_init_legacy(struct device_node *interrupt_parent) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct irq_domain *root_domain = 17062306a36Sopenharmony_ci irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0, 17162306a36Sopenharmony_ci &xtensa_mx_irq_domain_ops, 17262306a36Sopenharmony_ci &xtensa_mx_irq_chip); 17362306a36Sopenharmony_ci xtensa_mx_init_common(root_domain); 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int __init xtensa_mx_init(struct device_node *np, 17862306a36Sopenharmony_ci struct device_node *interrupt_parent) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct irq_domain *root_domain = 18162306a36Sopenharmony_ci irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops, 18262306a36Sopenharmony_ci &xtensa_mx_irq_chip); 18362306a36Sopenharmony_ci xtensa_mx_init_common(root_domain); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciIRQCHIP_DECLARE(xtensa_mx_irq_chip, "cdns,xtensa-mx", xtensa_mx_init); 187