162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 762306a36Sopenharmony_ci * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/irq.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <asm/irq_cpu.h> 1662306a36Sopenharmony_ci#include <asm/mipsregs.h> 1762306a36Sopenharmony_ci#include <bcm63xx_cpu.h> 1862306a36Sopenharmony_ci#include <bcm63xx_regs.h> 1962306a36Sopenharmony_ci#include <bcm63xx_io.h> 2062306a36Sopenharmony_ci#include <bcm63xx_irq.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ipic_lock); 2462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(epic_lock); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic u32 irq_stat_addr[2]; 2762306a36Sopenharmony_cistatic u32 irq_mask_addr[2]; 2862306a36Sopenharmony_cistatic void (*dispatch_internal)(int cpu); 2962306a36Sopenharmony_cistatic int is_ext_irq_cascaded; 3062306a36Sopenharmony_cistatic unsigned int ext_irq_count; 3162306a36Sopenharmony_cistatic unsigned int ext_irq_start, ext_irq_end; 3262306a36Sopenharmony_cistatic unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2; 3362306a36Sopenharmony_cistatic void (*internal_irq_mask)(struct irq_data *d); 3462306a36Sopenharmony_cistatic void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline u32 get_ext_irq_perf_reg(int irq) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (irq < 4) 4062306a36Sopenharmony_ci return ext_irq_cfg_reg1; 4162306a36Sopenharmony_ci return ext_irq_cfg_reg2; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic inline void handle_internal(int intbit) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci if (is_ext_irq_cascaded && 4762306a36Sopenharmony_ci intbit >= ext_irq_start && intbit <= ext_irq_end) 4862306a36Sopenharmony_ci do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE); 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci do_IRQ(intbit + IRQ_INTERNAL_BASE); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic inline int enable_irq_for_cpu(int cpu, struct irq_data *d, 5462306a36Sopenharmony_ci const struct cpumask *m) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci bool enable = cpu_online(cpu); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#ifdef CONFIG_SMP 5962306a36Sopenharmony_ci if (m) 6062306a36Sopenharmony_ci enable &= cpumask_test_cpu(cpu, m); 6162306a36Sopenharmony_ci else if (irqd_affinity_was_set(d)) 6262306a36Sopenharmony_ci enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d)); 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci return enable; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not 6962306a36Sopenharmony_ci * prioritize any interrupt relatively to another. the static counter 7062306a36Sopenharmony_ci * will resume the loop where it ended the last time we left this 7162306a36Sopenharmony_ci * function. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define BUILD_IPIC_INTERNAL(width) \ 7562306a36Sopenharmony_civoid __dispatch_internal_##width(int cpu) \ 7662306a36Sopenharmony_ci{ \ 7762306a36Sopenharmony_ci u32 pending[width / 32]; \ 7862306a36Sopenharmony_ci unsigned int src, tgt; \ 7962306a36Sopenharmony_ci bool irqs_pending = false; \ 8062306a36Sopenharmony_ci static unsigned int i[2]; \ 8162306a36Sopenharmony_ci unsigned int *next = &i[cpu]; \ 8262306a36Sopenharmony_ci unsigned long flags; \ 8362306a36Sopenharmony_ci \ 8462306a36Sopenharmony_ci /* read registers in reverse order */ \ 8562306a36Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 8662306a36Sopenharmony_ci for (src = 0, tgt = (width / 32); src < (width / 32); src++) { \ 8762306a36Sopenharmony_ci u32 val; \ 8862306a36Sopenharmony_ci \ 8962306a36Sopenharmony_ci val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \ 9062306a36Sopenharmony_ci val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \ 9162306a36Sopenharmony_ci pending[--tgt] = val; \ 9262306a36Sopenharmony_ci \ 9362306a36Sopenharmony_ci if (val) \ 9462306a36Sopenharmony_ci irqs_pending = true; \ 9562306a36Sopenharmony_ci } \ 9662306a36Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 9762306a36Sopenharmony_ci \ 9862306a36Sopenharmony_ci if (!irqs_pending) \ 9962306a36Sopenharmony_ci return; \ 10062306a36Sopenharmony_ci \ 10162306a36Sopenharmony_ci while (1) { \ 10262306a36Sopenharmony_ci unsigned int to_call = *next; \ 10362306a36Sopenharmony_ci \ 10462306a36Sopenharmony_ci *next = (*next + 1) & (width - 1); \ 10562306a36Sopenharmony_ci if (pending[to_call / 32] & (1 << (to_call & 0x1f))) { \ 10662306a36Sopenharmony_ci handle_internal(to_call); \ 10762306a36Sopenharmony_ci break; \ 10862306a36Sopenharmony_ci } \ 10962306a36Sopenharmony_ci } \ 11062306a36Sopenharmony_ci} \ 11162306a36Sopenharmony_ci \ 11262306a36Sopenharmony_cistatic void __internal_irq_mask_##width(struct irq_data *d) \ 11362306a36Sopenharmony_ci{ \ 11462306a36Sopenharmony_ci u32 val; \ 11562306a36Sopenharmony_ci unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ 11662306a36Sopenharmony_ci unsigned reg = (irq / 32) ^ (width/32 - 1); \ 11762306a36Sopenharmony_ci unsigned bit = irq & 0x1f; \ 11862306a36Sopenharmony_ci unsigned long flags; \ 11962306a36Sopenharmony_ci int cpu; \ 12062306a36Sopenharmony_ci \ 12162306a36Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 12262306a36Sopenharmony_ci for_each_present_cpu(cpu) { \ 12362306a36Sopenharmony_ci if (!irq_mask_addr[cpu]) \ 12462306a36Sopenharmony_ci break; \ 12562306a36Sopenharmony_ci \ 12662306a36Sopenharmony_ci val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ 12762306a36Sopenharmony_ci val &= ~(1 << bit); \ 12862306a36Sopenharmony_ci bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ 12962306a36Sopenharmony_ci } \ 13062306a36Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 13162306a36Sopenharmony_ci} \ 13262306a36Sopenharmony_ci \ 13362306a36Sopenharmony_cistatic void __internal_irq_unmask_##width(struct irq_data *d, \ 13462306a36Sopenharmony_ci const struct cpumask *m) \ 13562306a36Sopenharmony_ci{ \ 13662306a36Sopenharmony_ci u32 val; \ 13762306a36Sopenharmony_ci unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ 13862306a36Sopenharmony_ci unsigned reg = (irq / 32) ^ (width/32 - 1); \ 13962306a36Sopenharmony_ci unsigned bit = irq & 0x1f; \ 14062306a36Sopenharmony_ci unsigned long flags; \ 14162306a36Sopenharmony_ci int cpu; \ 14262306a36Sopenharmony_ci \ 14362306a36Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 14462306a36Sopenharmony_ci for_each_present_cpu(cpu) { \ 14562306a36Sopenharmony_ci if (!irq_mask_addr[cpu]) \ 14662306a36Sopenharmony_ci break; \ 14762306a36Sopenharmony_ci \ 14862306a36Sopenharmony_ci val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ 14962306a36Sopenharmony_ci if (enable_irq_for_cpu(cpu, d, m)) \ 15062306a36Sopenharmony_ci val |= (1 << bit); \ 15162306a36Sopenharmony_ci else \ 15262306a36Sopenharmony_ci val &= ~(1 << bit); \ 15362306a36Sopenharmony_ci bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ 15462306a36Sopenharmony_ci } \ 15562306a36Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciBUILD_IPIC_INTERNAL(32); 15962306a36Sopenharmony_ciBUILD_IPIC_INTERNAL(64); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci u32 cause; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci do { 16662306a36Sopenharmony_ci cause = read_c0_cause() & read_c0_status() & ST0_IM; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!cause) 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (cause & CAUSEF_IP7) 17262306a36Sopenharmony_ci do_IRQ(7); 17362306a36Sopenharmony_ci if (cause & CAUSEF_IP0) 17462306a36Sopenharmony_ci do_IRQ(0); 17562306a36Sopenharmony_ci if (cause & CAUSEF_IP1) 17662306a36Sopenharmony_ci do_IRQ(1); 17762306a36Sopenharmony_ci if (cause & CAUSEF_IP2) 17862306a36Sopenharmony_ci dispatch_internal(0); 17962306a36Sopenharmony_ci if (is_ext_irq_cascaded) { 18062306a36Sopenharmony_ci if (cause & CAUSEF_IP3) 18162306a36Sopenharmony_ci dispatch_internal(1); 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci if (cause & CAUSEF_IP3) 18462306a36Sopenharmony_ci do_IRQ(IRQ_EXT_0); 18562306a36Sopenharmony_ci if (cause & CAUSEF_IP4) 18662306a36Sopenharmony_ci do_IRQ(IRQ_EXT_1); 18762306a36Sopenharmony_ci if (cause & CAUSEF_IP5) 18862306a36Sopenharmony_ci do_IRQ(IRQ_EXT_2); 18962306a36Sopenharmony_ci if (cause & CAUSEF_IP6) 19062306a36Sopenharmony_ci do_IRQ(IRQ_EXT_3); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci } while (1); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * internal IRQs operations: only mask/unmask on PERF irq mask 19762306a36Sopenharmony_ci * register. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cistatic void bcm63xx_internal_irq_mask(struct irq_data *d) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci internal_irq_mask(d); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void bcm63xx_internal_irq_unmask(struct irq_data *d) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci internal_irq_unmask(d, NULL); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* 21062306a36Sopenharmony_ci * external IRQs operations: mask/unmask and clear on PERF external 21162306a36Sopenharmony_ci * irq control register. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void bcm63xx_external_irq_mask(struct irq_data *d) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 21662306a36Sopenharmony_ci u32 reg, regaddr; 21762306a36Sopenharmony_ci unsigned long flags; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 22062306a36Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 22162306a36Sopenharmony_ci reg = bcm_perf_readl(regaddr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (BCMCPU_IS_6348()) 22462306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4); 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_MASK(irq % 4); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci bcm_perf_writel(reg, regaddr); 22962306a36Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (is_ext_irq_cascaded) 23262306a36Sopenharmony_ci internal_irq_mask(irq_get_irq_data(irq + ext_irq_start)); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void bcm63xx_external_irq_unmask(struct irq_data *d) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 23862306a36Sopenharmony_ci u32 reg, regaddr; 23962306a36Sopenharmony_ci unsigned long flags; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 24262306a36Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 24362306a36Sopenharmony_ci reg = bcm_perf_readl(regaddr); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (BCMCPU_IS_6348()) 24662306a36Sopenharmony_ci reg |= EXTIRQ_CFG_MASK_6348(irq % 4); 24762306a36Sopenharmony_ci else 24862306a36Sopenharmony_ci reg |= EXTIRQ_CFG_MASK(irq % 4); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci bcm_perf_writel(reg, regaddr); 25162306a36Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (is_ext_irq_cascaded) 25462306a36Sopenharmony_ci internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start), 25562306a36Sopenharmony_ci NULL); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void bcm63xx_external_irq_clear(struct irq_data *d) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 26162306a36Sopenharmony_ci u32 reg, regaddr; 26262306a36Sopenharmony_ci unsigned long flags; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 26562306a36Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 26662306a36Sopenharmony_ci reg = bcm_perf_readl(regaddr); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (BCMCPU_IS_6348()) 26962306a36Sopenharmony_ci reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4); 27062306a36Sopenharmony_ci else 27162306a36Sopenharmony_ci reg |= EXTIRQ_CFG_CLEAR(irq % 4); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bcm_perf_writel(reg, regaddr); 27462306a36Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int bcm63xx_external_irq_set_type(struct irq_data *d, 27862306a36Sopenharmony_ci unsigned int flow_type) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 28162306a36Sopenharmony_ci u32 reg, regaddr; 28262306a36Sopenharmony_ci int levelsense, sense, bothedge; 28362306a36Sopenharmony_ci unsigned long flags; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci flow_type &= IRQ_TYPE_SENSE_MASK; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 28862306a36Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci levelsense = sense = bothedge = 0; 29162306a36Sopenharmony_ci switch (flow_type) { 29262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 29362306a36Sopenharmony_ci bothedge = 1; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 29762306a36Sopenharmony_ci sense = 1; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 30462306a36Sopenharmony_ci levelsense = 1; 30562306a36Sopenharmony_ci sense = 1; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 30962306a36Sopenharmony_ci levelsense = 1; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci pr_err("bogus flow type combination given !\n"); 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 31862306a36Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 31962306a36Sopenharmony_ci reg = bcm_perf_readl(regaddr); 32062306a36Sopenharmony_ci irq %= 4; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 32362306a36Sopenharmony_ci case BCM6348_CPU_ID: 32462306a36Sopenharmony_ci if (levelsense) 32562306a36Sopenharmony_ci reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq); 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq); 32862306a36Sopenharmony_ci if (sense) 32962306a36Sopenharmony_ci reg |= EXTIRQ_CFG_SENSE_6348(irq); 33062306a36Sopenharmony_ci else 33162306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_SENSE_6348(irq); 33262306a36Sopenharmony_ci if (bothedge) 33362306a36Sopenharmony_ci reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq); 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq); 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci case BCM3368_CPU_ID: 33962306a36Sopenharmony_ci case BCM6328_CPU_ID: 34062306a36Sopenharmony_ci case BCM6338_CPU_ID: 34162306a36Sopenharmony_ci case BCM6345_CPU_ID: 34262306a36Sopenharmony_ci case BCM6358_CPU_ID: 34362306a36Sopenharmony_ci case BCM6362_CPU_ID: 34462306a36Sopenharmony_ci case BCM6368_CPU_ID: 34562306a36Sopenharmony_ci if (levelsense) 34662306a36Sopenharmony_ci reg |= EXTIRQ_CFG_LEVELSENSE(irq); 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 34962306a36Sopenharmony_ci if (sense) 35062306a36Sopenharmony_ci reg |= EXTIRQ_CFG_SENSE(irq); 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_SENSE(irq); 35362306a36Sopenharmony_ci if (bothedge) 35462306a36Sopenharmony_ci reg |= EXTIRQ_CFG_BOTHEDGE(irq); 35562306a36Sopenharmony_ci else 35662306a36Sopenharmony_ci reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci default: 35962306a36Sopenharmony_ci BUG(); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci bcm_perf_writel(reg, regaddr); 36362306a36Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci irqd_set_trigger_type(d, flow_type); 36662306a36Sopenharmony_ci if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 36762306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return IRQ_SET_MASK_OK_NOCOPY; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci#ifdef CONFIG_SMP 37562306a36Sopenharmony_cistatic int bcm63xx_internal_set_affinity(struct irq_data *data, 37662306a36Sopenharmony_ci const struct cpumask *dest, 37762306a36Sopenharmony_ci bool force) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci if (!irqd_irq_disabled(data)) 38062306a36Sopenharmony_ci internal_irq_unmask(data, dest); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic struct irq_chip bcm63xx_internal_irq_chip = { 38762306a36Sopenharmony_ci .name = "bcm63xx_ipic", 38862306a36Sopenharmony_ci .irq_mask = bcm63xx_internal_irq_mask, 38962306a36Sopenharmony_ci .irq_unmask = bcm63xx_internal_irq_unmask, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic struct irq_chip bcm63xx_external_irq_chip = { 39362306a36Sopenharmony_ci .name = "bcm63xx_epic", 39462306a36Sopenharmony_ci .irq_ack = bcm63xx_external_irq_clear, 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci .irq_mask = bcm63xx_external_irq_mask, 39762306a36Sopenharmony_ci .irq_unmask = bcm63xx_external_irq_unmask, 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci .irq_set_type = bcm63xx_external_irq_set_type, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void bcm63xx_init_irq(void) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int irq_bits; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF); 40762306a36Sopenharmony_ci irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF); 40862306a36Sopenharmony_ci irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF); 40962306a36Sopenharmony_ci irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 41262306a36Sopenharmony_ci case BCM3368_CPU_ID: 41362306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_3368_REG; 41462306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_3368_REG; 41562306a36Sopenharmony_ci irq_stat_addr[1] = 0; 41662306a36Sopenharmony_ci irq_mask_addr[1] = 0; 41762306a36Sopenharmony_ci irq_bits = 32; 41862306a36Sopenharmony_ci ext_irq_count = 4; 41962306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case BCM6328_CPU_ID: 42262306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0); 42362306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0); 42462306a36Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1); 42562306a36Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1); 42662306a36Sopenharmony_ci irq_bits = 64; 42762306a36Sopenharmony_ci ext_irq_count = 4; 42862306a36Sopenharmony_ci is_ext_irq_cascaded = 1; 42962306a36Sopenharmony_ci ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE; 43062306a36Sopenharmony_ci ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE; 43162306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case BCM6338_CPU_ID: 43462306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6338_REG; 43562306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6338_REG; 43662306a36Sopenharmony_ci irq_stat_addr[1] = 0; 43762306a36Sopenharmony_ci irq_mask_addr[1] = 0; 43862306a36Sopenharmony_ci irq_bits = 32; 43962306a36Sopenharmony_ci ext_irq_count = 4; 44062306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338; 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case BCM6345_CPU_ID: 44362306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6345_REG; 44462306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6345_REG; 44562306a36Sopenharmony_ci irq_stat_addr[1] = 0; 44662306a36Sopenharmony_ci irq_mask_addr[1] = 0; 44762306a36Sopenharmony_ci irq_bits = 32; 44862306a36Sopenharmony_ci ext_irq_count = 4; 44962306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345; 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci case BCM6348_CPU_ID: 45262306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6348_REG; 45362306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6348_REG; 45462306a36Sopenharmony_ci irq_stat_addr[1] = 0; 45562306a36Sopenharmony_ci irq_mask_addr[1] = 0; 45662306a36Sopenharmony_ci irq_bits = 32; 45762306a36Sopenharmony_ci ext_irq_count = 4; 45862306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case BCM6358_CPU_ID: 46162306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0); 46262306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0); 46362306a36Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1); 46462306a36Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1); 46562306a36Sopenharmony_ci irq_bits = 32; 46662306a36Sopenharmony_ci ext_irq_count = 4; 46762306a36Sopenharmony_ci is_ext_irq_cascaded = 1; 46862306a36Sopenharmony_ci ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE; 46962306a36Sopenharmony_ci ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE; 47062306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci case BCM6362_CPU_ID: 47362306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0); 47462306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0); 47562306a36Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1); 47662306a36Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1); 47762306a36Sopenharmony_ci irq_bits = 64; 47862306a36Sopenharmony_ci ext_irq_count = 4; 47962306a36Sopenharmony_ci is_ext_irq_cascaded = 1; 48062306a36Sopenharmony_ci ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE; 48162306a36Sopenharmony_ci ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE; 48262306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case BCM6368_CPU_ID: 48562306a36Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0); 48662306a36Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0); 48762306a36Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1); 48862306a36Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1); 48962306a36Sopenharmony_ci irq_bits = 64; 49062306a36Sopenharmony_ci ext_irq_count = 6; 49162306a36Sopenharmony_ci is_ext_irq_cascaded = 1; 49262306a36Sopenharmony_ci ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE; 49362306a36Sopenharmony_ci ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE; 49462306a36Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368; 49562306a36Sopenharmony_ci ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci default: 49862306a36Sopenharmony_ci BUG(); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (irq_bits == 32) { 50262306a36Sopenharmony_ci dispatch_internal = __dispatch_internal_32; 50362306a36Sopenharmony_ci internal_irq_mask = __internal_irq_mask_32; 50462306a36Sopenharmony_ci internal_irq_unmask = __internal_irq_unmask_32; 50562306a36Sopenharmony_ci } else { 50662306a36Sopenharmony_ci dispatch_internal = __dispatch_internal_64; 50762306a36Sopenharmony_ci internal_irq_mask = __internal_irq_mask_64; 50862306a36Sopenharmony_ci internal_irq_unmask = __internal_irq_unmask_64; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_civoid __init arch_init_irq(void) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci int i, irq; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci bcm63xx_init_irq(); 51762306a36Sopenharmony_ci mips_cpu_irq_init(); 51862306a36Sopenharmony_ci for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i) 51962306a36Sopenharmony_ci irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip, 52062306a36Sopenharmony_ci handle_level_irq); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i) 52362306a36Sopenharmony_ci irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip, 52462306a36Sopenharmony_ci handle_edge_irq); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (!is_ext_irq_cascaded) { 52762306a36Sopenharmony_ci for (i = 3; i < 3 + ext_irq_count; ++i) { 52862306a36Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + i; 52962306a36Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, 53062306a36Sopenharmony_ci "cascade_extirq", NULL)) { 53162306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade_extirq)\n", 53262306a36Sopenharmony_ci irq); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + 2; 53862306a36Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip2", NULL)) 53962306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade_ip2)\n", irq); 54062306a36Sopenharmony_ci#ifdef CONFIG_SMP 54162306a36Sopenharmony_ci if (is_ext_irq_cascaded) { 54262306a36Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + 3; 54362306a36Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip3", 54462306a36Sopenharmony_ci NULL)) 54562306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade_ip3)\n", irq); 54662306a36Sopenharmony_ci bcm63xx_internal_irq_chip.irq_set_affinity = 54762306a36Sopenharmony_ci bcm63xx_internal_set_affinity; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci cpumask_clear(irq_default_affinity); 55062306a36Sopenharmony_ci cpumask_set_cpu(smp_processor_id(), irq_default_affinity); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci#endif 55362306a36Sopenharmony_ci} 554