18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h> 168c2ecf20Sopenharmony_ci#include <asm/mipsregs.h> 178c2ecf20Sopenharmony_ci#include <bcm63xx_cpu.h> 188c2ecf20Sopenharmony_ci#include <bcm63xx_regs.h> 198c2ecf20Sopenharmony_ci#include <bcm63xx_io.h> 208c2ecf20Sopenharmony_ci#include <bcm63xx_irq.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ipic_lock); 248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(epic_lock); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic u32 irq_stat_addr[2]; 278c2ecf20Sopenharmony_cistatic u32 irq_mask_addr[2]; 288c2ecf20Sopenharmony_cistatic void (*dispatch_internal)(int cpu); 298c2ecf20Sopenharmony_cistatic int is_ext_irq_cascaded; 308c2ecf20Sopenharmony_cistatic unsigned int ext_irq_count; 318c2ecf20Sopenharmony_cistatic unsigned int ext_irq_start, ext_irq_end; 328c2ecf20Sopenharmony_cistatic unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2; 338c2ecf20Sopenharmony_cistatic void (*internal_irq_mask)(struct irq_data *d); 348c2ecf20Sopenharmony_cistatic void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline u32 get_ext_irq_perf_reg(int irq) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (irq < 4) 408c2ecf20Sopenharmony_ci return ext_irq_cfg_reg1; 418c2ecf20Sopenharmony_ci return ext_irq_cfg_reg2; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline void handle_internal(int intbit) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci if (is_ext_irq_cascaded && 478c2ecf20Sopenharmony_ci intbit >= ext_irq_start && intbit <= ext_irq_end) 488c2ecf20Sopenharmony_ci do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE); 498c2ecf20Sopenharmony_ci else 508c2ecf20Sopenharmony_ci do_IRQ(intbit + IRQ_INTERNAL_BASE); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline int enable_irq_for_cpu(int cpu, struct irq_data *d, 548c2ecf20Sopenharmony_ci const struct cpumask *m) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci bool enable = cpu_online(cpu); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 598c2ecf20Sopenharmony_ci if (m) 608c2ecf20Sopenharmony_ci enable &= cpumask_test_cpu(cpu, m); 618c2ecf20Sopenharmony_ci else if (irqd_affinity_was_set(d)) 628c2ecf20Sopenharmony_ci enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d)); 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci return enable; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not 698c2ecf20Sopenharmony_ci * prioritize any interrupt relatively to another. the static counter 708c2ecf20Sopenharmony_ci * will resume the loop where it ended the last time we left this 718c2ecf20Sopenharmony_ci * function. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define BUILD_IPIC_INTERNAL(width) \ 758c2ecf20Sopenharmony_civoid __dispatch_internal_##width(int cpu) \ 768c2ecf20Sopenharmony_ci{ \ 778c2ecf20Sopenharmony_ci u32 pending[width / 32]; \ 788c2ecf20Sopenharmony_ci unsigned int src, tgt; \ 798c2ecf20Sopenharmony_ci bool irqs_pending = false; \ 808c2ecf20Sopenharmony_ci static unsigned int i[2]; \ 818c2ecf20Sopenharmony_ci unsigned int *next = &i[cpu]; \ 828c2ecf20Sopenharmony_ci unsigned long flags; \ 838c2ecf20Sopenharmony_ci \ 848c2ecf20Sopenharmony_ci /* read registers in reverse order */ \ 858c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 868c2ecf20Sopenharmony_ci for (src = 0, tgt = (width / 32); src < (width / 32); src++) { \ 878c2ecf20Sopenharmony_ci u32 val; \ 888c2ecf20Sopenharmony_ci \ 898c2ecf20Sopenharmony_ci val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \ 908c2ecf20Sopenharmony_ci val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \ 918c2ecf20Sopenharmony_ci pending[--tgt] = val; \ 928c2ecf20Sopenharmony_ci \ 938c2ecf20Sopenharmony_ci if (val) \ 948c2ecf20Sopenharmony_ci irqs_pending = true; \ 958c2ecf20Sopenharmony_ci } \ 968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 978c2ecf20Sopenharmony_ci \ 988c2ecf20Sopenharmony_ci if (!irqs_pending) \ 998c2ecf20Sopenharmony_ci return; \ 1008c2ecf20Sopenharmony_ci \ 1018c2ecf20Sopenharmony_ci while (1) { \ 1028c2ecf20Sopenharmony_ci unsigned int to_call = *next; \ 1038c2ecf20Sopenharmony_ci \ 1048c2ecf20Sopenharmony_ci *next = (*next + 1) & (width - 1); \ 1058c2ecf20Sopenharmony_ci if (pending[to_call / 32] & (1 << (to_call & 0x1f))) { \ 1068c2ecf20Sopenharmony_ci handle_internal(to_call); \ 1078c2ecf20Sopenharmony_ci break; \ 1088c2ecf20Sopenharmony_ci } \ 1098c2ecf20Sopenharmony_ci } \ 1108c2ecf20Sopenharmony_ci} \ 1118c2ecf20Sopenharmony_ci \ 1128c2ecf20Sopenharmony_cistatic void __internal_irq_mask_##width(struct irq_data *d) \ 1138c2ecf20Sopenharmony_ci{ \ 1148c2ecf20Sopenharmony_ci u32 val; \ 1158c2ecf20Sopenharmony_ci unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ 1168c2ecf20Sopenharmony_ci unsigned reg = (irq / 32) ^ (width/32 - 1); \ 1178c2ecf20Sopenharmony_ci unsigned bit = irq & 0x1f; \ 1188c2ecf20Sopenharmony_ci unsigned long flags; \ 1198c2ecf20Sopenharmony_ci int cpu; \ 1208c2ecf20Sopenharmony_ci \ 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 1228c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { \ 1238c2ecf20Sopenharmony_ci if (!irq_mask_addr[cpu]) \ 1248c2ecf20Sopenharmony_ci break; \ 1258c2ecf20Sopenharmony_ci \ 1268c2ecf20Sopenharmony_ci val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ 1278c2ecf20Sopenharmony_ci val &= ~(1 << bit); \ 1288c2ecf20Sopenharmony_ci bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ 1298c2ecf20Sopenharmony_ci } \ 1308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 1318c2ecf20Sopenharmony_ci} \ 1328c2ecf20Sopenharmony_ci \ 1338c2ecf20Sopenharmony_cistatic void __internal_irq_unmask_##width(struct irq_data *d, \ 1348c2ecf20Sopenharmony_ci const struct cpumask *m) \ 1358c2ecf20Sopenharmony_ci{ \ 1368c2ecf20Sopenharmony_ci u32 val; \ 1378c2ecf20Sopenharmony_ci unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ 1388c2ecf20Sopenharmony_ci unsigned reg = (irq / 32) ^ (width/32 - 1); \ 1398c2ecf20Sopenharmony_ci unsigned bit = irq & 0x1f; \ 1408c2ecf20Sopenharmony_ci unsigned long flags; \ 1418c2ecf20Sopenharmony_ci int cpu; \ 1428c2ecf20Sopenharmony_ci \ 1438c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipic_lock, flags); \ 1448c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { \ 1458c2ecf20Sopenharmony_ci if (!irq_mask_addr[cpu]) \ 1468c2ecf20Sopenharmony_ci break; \ 1478c2ecf20Sopenharmony_ci \ 1488c2ecf20Sopenharmony_ci val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ 1498c2ecf20Sopenharmony_ci if (enable_irq_for_cpu(cpu, d, m)) \ 1508c2ecf20Sopenharmony_ci val |= (1 << bit); \ 1518c2ecf20Sopenharmony_ci else \ 1528c2ecf20Sopenharmony_ci val &= ~(1 << bit); \ 1538c2ecf20Sopenharmony_ci bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ 1548c2ecf20Sopenharmony_ci } \ 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipic_lock, flags); \ 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciBUILD_IPIC_INTERNAL(32); 1598c2ecf20Sopenharmony_ciBUILD_IPIC_INTERNAL(64); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciasmlinkage void plat_irq_dispatch(void) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u32 cause; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci do { 1668c2ecf20Sopenharmony_ci cause = read_c0_cause() & read_c0_status() & ST0_IM; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!cause) 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP7) 1728c2ecf20Sopenharmony_ci do_IRQ(7); 1738c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP0) 1748c2ecf20Sopenharmony_ci do_IRQ(0); 1758c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP1) 1768c2ecf20Sopenharmony_ci do_IRQ(1); 1778c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP2) 1788c2ecf20Sopenharmony_ci dispatch_internal(0); 1798c2ecf20Sopenharmony_ci if (is_ext_irq_cascaded) { 1808c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP3) 1818c2ecf20Sopenharmony_ci dispatch_internal(1); 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP3) 1848c2ecf20Sopenharmony_ci do_IRQ(IRQ_EXT_0); 1858c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP4) 1868c2ecf20Sopenharmony_ci do_IRQ(IRQ_EXT_1); 1878c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP5) 1888c2ecf20Sopenharmony_ci do_IRQ(IRQ_EXT_2); 1898c2ecf20Sopenharmony_ci if (cause & CAUSEF_IP6) 1908c2ecf20Sopenharmony_ci do_IRQ(IRQ_EXT_3); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } while (1); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * internal IRQs operations: only mask/unmask on PERF irq mask 1978c2ecf20Sopenharmony_ci * register. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void bcm63xx_internal_irq_mask(struct irq_data *d) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci internal_irq_mask(d); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void bcm63xx_internal_irq_unmask(struct irq_data *d) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci internal_irq_unmask(d, NULL); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * external IRQs operations: mask/unmask and clear on PERF external 2118c2ecf20Sopenharmony_ci * irq control register. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_mask(struct irq_data *d) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 2168c2ecf20Sopenharmony_ci u32 reg, regaddr; 2178c2ecf20Sopenharmony_ci unsigned long flags; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 2208c2ecf20Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 2218c2ecf20Sopenharmony_ci reg = bcm_perf_readl(regaddr); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (BCMCPU_IS_6348()) 2248c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4); 2258c2ecf20Sopenharmony_ci else 2268c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_MASK(irq % 4); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci bcm_perf_writel(reg, regaddr); 2298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (is_ext_irq_cascaded) 2328c2ecf20Sopenharmony_ci internal_irq_mask(irq_get_irq_data(irq + ext_irq_start)); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_unmask(struct irq_data *d) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 2388c2ecf20Sopenharmony_ci u32 reg, regaddr; 2398c2ecf20Sopenharmony_ci unsigned long flags; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 2428c2ecf20Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 2438c2ecf20Sopenharmony_ci reg = bcm_perf_readl(regaddr); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (BCMCPU_IS_6348()) 2468c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_MASK_6348(irq % 4); 2478c2ecf20Sopenharmony_ci else 2488c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_MASK(irq % 4); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci bcm_perf_writel(reg, regaddr); 2518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (is_ext_irq_cascaded) 2548c2ecf20Sopenharmony_ci internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start), 2558c2ecf20Sopenharmony_ci NULL); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_clear(struct irq_data *d) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 2618c2ecf20Sopenharmony_ci u32 reg, regaddr; 2628c2ecf20Sopenharmony_ci unsigned long flags; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 2658c2ecf20Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 2668c2ecf20Sopenharmony_ci reg = bcm_perf_readl(regaddr); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (BCMCPU_IS_6348()) 2698c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4); 2708c2ecf20Sopenharmony_ci else 2718c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_CLEAR(irq % 4); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci bcm_perf_writel(reg, regaddr); 2748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int bcm63xx_external_irq_set_type(struct irq_data *d, 2788c2ecf20Sopenharmony_ci unsigned int flow_type) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; 2818c2ecf20Sopenharmony_ci u32 reg, regaddr; 2828c2ecf20Sopenharmony_ci int levelsense, sense, bothedge; 2838c2ecf20Sopenharmony_ci unsigned long flags; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci flow_type &= IRQ_TYPE_SENSE_MASK; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 2888c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci levelsense = sense = bothedge = 0; 2918c2ecf20Sopenharmony_ci switch (flow_type) { 2928c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 2938c2ecf20Sopenharmony_ci bothedge = 1; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2978c2ecf20Sopenharmony_ci sense = 1; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 3048c2ecf20Sopenharmony_ci levelsense = 1; 3058c2ecf20Sopenharmony_ci sense = 1; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 3098c2ecf20Sopenharmony_ci levelsense = 1; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci pr_err("bogus flow type combination given !\n"); 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci regaddr = get_ext_irq_perf_reg(irq); 3188c2ecf20Sopenharmony_ci spin_lock_irqsave(&epic_lock, flags); 3198c2ecf20Sopenharmony_ci reg = bcm_perf_readl(regaddr); 3208c2ecf20Sopenharmony_ci irq %= 4; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 3238c2ecf20Sopenharmony_ci case BCM6348_CPU_ID: 3248c2ecf20Sopenharmony_ci if (levelsense) 3258c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq); 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq); 3288c2ecf20Sopenharmony_ci if (sense) 3298c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_SENSE_6348(irq); 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_SENSE_6348(irq); 3328c2ecf20Sopenharmony_ci if (bothedge) 3338c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq); 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci case BCM3368_CPU_ID: 3398c2ecf20Sopenharmony_ci case BCM6328_CPU_ID: 3408c2ecf20Sopenharmony_ci case BCM6338_CPU_ID: 3418c2ecf20Sopenharmony_ci case BCM6345_CPU_ID: 3428c2ecf20Sopenharmony_ci case BCM6358_CPU_ID: 3438c2ecf20Sopenharmony_ci case BCM6362_CPU_ID: 3448c2ecf20Sopenharmony_ci case BCM6368_CPU_ID: 3458c2ecf20Sopenharmony_ci if (levelsense) 3468c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_LEVELSENSE(irq); 3478c2ecf20Sopenharmony_ci else 3488c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 3498c2ecf20Sopenharmony_ci if (sense) 3508c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_SENSE(irq); 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_SENSE(irq); 3538c2ecf20Sopenharmony_ci if (bothedge) 3548c2ecf20Sopenharmony_ci reg |= EXTIRQ_CFG_BOTHEDGE(irq); 3558c2ecf20Sopenharmony_ci else 3568c2ecf20Sopenharmony_ci reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci BUG(); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci bcm_perf_writel(reg, regaddr); 3638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&epic_lock, flags); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci irqd_set_trigger_type(d, flow_type); 3668c2ecf20Sopenharmony_ci if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 3678c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 3688c2ecf20Sopenharmony_ci else 3698c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK_NOCOPY; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 3758c2ecf20Sopenharmony_cistatic int bcm63xx_internal_set_affinity(struct irq_data *data, 3768c2ecf20Sopenharmony_ci const struct cpumask *dest, 3778c2ecf20Sopenharmony_ci bool force) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci if (!irqd_irq_disabled(data)) 3808c2ecf20Sopenharmony_ci internal_irq_unmask(data, dest); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci#endif 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic struct irq_chip bcm63xx_internal_irq_chip = { 3878c2ecf20Sopenharmony_ci .name = "bcm63xx_ipic", 3888c2ecf20Sopenharmony_ci .irq_mask = bcm63xx_internal_irq_mask, 3898c2ecf20Sopenharmony_ci .irq_unmask = bcm63xx_internal_irq_unmask, 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic struct irq_chip bcm63xx_external_irq_chip = { 3938c2ecf20Sopenharmony_ci .name = "bcm63xx_epic", 3948c2ecf20Sopenharmony_ci .irq_ack = bcm63xx_external_irq_clear, 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci .irq_mask = bcm63xx_external_irq_mask, 3978c2ecf20Sopenharmony_ci .irq_unmask = bcm63xx_external_irq_unmask, 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci .irq_set_type = bcm63xx_external_irq_set_type, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void bcm63xx_init_irq(void) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int irq_bits; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF); 4078c2ecf20Sopenharmony_ci irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF); 4088c2ecf20Sopenharmony_ci irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF); 4098c2ecf20Sopenharmony_ci irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 4128c2ecf20Sopenharmony_ci case BCM3368_CPU_ID: 4138c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_3368_REG; 4148c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_3368_REG; 4158c2ecf20Sopenharmony_ci irq_stat_addr[1] = 0; 4168c2ecf20Sopenharmony_ci irq_mask_addr[1] = 0; 4178c2ecf20Sopenharmony_ci irq_bits = 32; 4188c2ecf20Sopenharmony_ci ext_irq_count = 4; 4198c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368; 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci case BCM6328_CPU_ID: 4228c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0); 4238c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0); 4248c2ecf20Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1); 4258c2ecf20Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1); 4268c2ecf20Sopenharmony_ci irq_bits = 64; 4278c2ecf20Sopenharmony_ci ext_irq_count = 4; 4288c2ecf20Sopenharmony_ci is_ext_irq_cascaded = 1; 4298c2ecf20Sopenharmony_ci ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE; 4308c2ecf20Sopenharmony_ci ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE; 4318c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case BCM6338_CPU_ID: 4348c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6338_REG; 4358c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6338_REG; 4368c2ecf20Sopenharmony_ci irq_stat_addr[1] = 0; 4378c2ecf20Sopenharmony_ci irq_mask_addr[1] = 0; 4388c2ecf20Sopenharmony_ci irq_bits = 32; 4398c2ecf20Sopenharmony_ci ext_irq_count = 4; 4408c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338; 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci case BCM6345_CPU_ID: 4438c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6345_REG; 4448c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6345_REG; 4458c2ecf20Sopenharmony_ci irq_stat_addr[1] = 0; 4468c2ecf20Sopenharmony_ci irq_mask_addr[1] = 0; 4478c2ecf20Sopenharmony_ci irq_bits = 32; 4488c2ecf20Sopenharmony_ci ext_irq_count = 4; 4498c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci case BCM6348_CPU_ID: 4528c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6348_REG; 4538c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6348_REG; 4548c2ecf20Sopenharmony_ci irq_stat_addr[1] = 0; 4558c2ecf20Sopenharmony_ci irq_mask_addr[1] = 0; 4568c2ecf20Sopenharmony_ci irq_bits = 32; 4578c2ecf20Sopenharmony_ci ext_irq_count = 4; 4588c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case BCM6358_CPU_ID: 4618c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0); 4628c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0); 4638c2ecf20Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1); 4648c2ecf20Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1); 4658c2ecf20Sopenharmony_ci irq_bits = 32; 4668c2ecf20Sopenharmony_ci ext_irq_count = 4; 4678c2ecf20Sopenharmony_ci is_ext_irq_cascaded = 1; 4688c2ecf20Sopenharmony_ci ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE; 4698c2ecf20Sopenharmony_ci ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE; 4708c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case BCM6362_CPU_ID: 4738c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0); 4748c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0); 4758c2ecf20Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1); 4768c2ecf20Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1); 4778c2ecf20Sopenharmony_ci irq_bits = 64; 4788c2ecf20Sopenharmony_ci ext_irq_count = 4; 4798c2ecf20Sopenharmony_ci is_ext_irq_cascaded = 1; 4808c2ecf20Sopenharmony_ci ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE; 4818c2ecf20Sopenharmony_ci ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE; 4828c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case BCM6368_CPU_ID: 4858c2ecf20Sopenharmony_ci irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0); 4868c2ecf20Sopenharmony_ci irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0); 4878c2ecf20Sopenharmony_ci irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1); 4888c2ecf20Sopenharmony_ci irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1); 4898c2ecf20Sopenharmony_ci irq_bits = 64; 4908c2ecf20Sopenharmony_ci ext_irq_count = 6; 4918c2ecf20Sopenharmony_ci is_ext_irq_cascaded = 1; 4928c2ecf20Sopenharmony_ci ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE; 4938c2ecf20Sopenharmony_ci ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE; 4948c2ecf20Sopenharmony_ci ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368; 4958c2ecf20Sopenharmony_ci ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci default: 4988c2ecf20Sopenharmony_ci BUG(); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (irq_bits == 32) { 5028c2ecf20Sopenharmony_ci dispatch_internal = __dispatch_internal_32; 5038c2ecf20Sopenharmony_ci internal_irq_mask = __internal_irq_mask_32; 5048c2ecf20Sopenharmony_ci internal_irq_unmask = __internal_irq_unmask_32; 5058c2ecf20Sopenharmony_ci } else { 5068c2ecf20Sopenharmony_ci dispatch_internal = __dispatch_internal_64; 5078c2ecf20Sopenharmony_ci internal_irq_mask = __internal_irq_mask_64; 5088c2ecf20Sopenharmony_ci internal_irq_unmask = __internal_irq_unmask_64; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_civoid __init arch_init_irq(void) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci int i, irq; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci bcm63xx_init_irq(); 5178c2ecf20Sopenharmony_ci mips_cpu_irq_init(); 5188c2ecf20Sopenharmony_ci for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i) 5198c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip, 5208c2ecf20Sopenharmony_ci handle_level_irq); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i) 5238c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip, 5248c2ecf20Sopenharmony_ci handle_edge_irq); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (!is_ext_irq_cascaded) { 5278c2ecf20Sopenharmony_ci for (i = 3; i < 3 + ext_irq_count; ++i) { 5288c2ecf20Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + i; 5298c2ecf20Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, 5308c2ecf20Sopenharmony_ci "cascade_extirq", NULL)) { 5318c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade_extirq)\n", 5328c2ecf20Sopenharmony_ci irq); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + 2; 5388c2ecf20Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip2", NULL)) 5398c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade_ip2)\n", irq); 5408c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 5418c2ecf20Sopenharmony_ci if (is_ext_irq_cascaded) { 5428c2ecf20Sopenharmony_ci irq = MIPS_CPU_IRQ_BASE + 3; 5438c2ecf20Sopenharmony_ci if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip3", 5448c2ecf20Sopenharmony_ci NULL)) 5458c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade_ip3)\n", irq); 5468c2ecf20Sopenharmony_ci bcm63xx_internal_irq_chip.irq_set_affinity = 5478c2ecf20Sopenharmony_ci bcm63xx_internal_set_affinity; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci cpumask_clear(irq_default_affinity); 5508c2ecf20Sopenharmony_ci cpumask_set_cpu(smp_processor_id(), irq_default_affinity); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci#endif 5538c2ecf20Sopenharmony_ci} 554