18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/powerpc/kernel/mpic.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Driver for interrupt controllers following the OpenPIC standard, the 58c2ecf20Sopenharmony_ci * common implementation being IBM's MPIC. This driver also can deal 68c2ecf20Sopenharmony_ci * with various broken implementations of this HW. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. 98c2ecf20Sopenharmony_ci * Copyright 2010-2012 Freescale Semiconductor, Inc. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 128c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 138c2ecf20Sopenharmony_ci * for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#undef DEBUG 178c2ecf20Sopenharmony_ci#undef DEBUG_IPI 188c2ecf20Sopenharmony_ci#undef DEBUG_IRQ 198c2ecf20Sopenharmony_ci#undef DEBUG_LOW 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/irq.h> 258c2ecf20Sopenharmony_ci#include <linux/smp.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/pci.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 318c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 328c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 358c2ecf20Sopenharmony_ci#include <asm/signal.h> 368c2ecf20Sopenharmony_ci#include <asm/io.h> 378c2ecf20Sopenharmony_ci#include <asm/irq.h> 388c2ecf20Sopenharmony_ci#include <asm/machdep.h> 398c2ecf20Sopenharmony_ci#include <asm/mpic.h> 408c2ecf20Sopenharmony_ci#include <asm/smp.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "mpic.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef DEBUG 458c2ecf20Sopenharmony_ci#define DBG(fmt...) printk(fmt) 468c2ecf20Sopenharmony_ci#else 478c2ecf20Sopenharmony_ci#define DBG(fmt...) 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct bus_type mpic_subsys = { 518c2ecf20Sopenharmony_ci .name = "mpic", 528c2ecf20Sopenharmony_ci .dev_name = "mpic", 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_subsys); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct mpic *mpics; 578c2ecf20Sopenharmony_cistatic struct mpic *mpic_primary; 588c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(mpic_lock); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 /* XXX for now */ 618c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQ_ALL_CPUS 628c2ecf20Sopenharmony_ci#define distribute_irqs (1) 638c2ecf20Sopenharmony_ci#else 648c2ecf20Sopenharmony_ci#define distribute_irqs (0) 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci#endif 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_WEIRD 698c2ecf20Sopenharmony_cistatic u32 mpic_infos[][MPIC_IDX_END] = { 708c2ecf20Sopenharmony_ci [0] = { /* Original OpenPIC compatible MPIC */ 718c2ecf20Sopenharmony_ci MPIC_GREG_BASE, 728c2ecf20Sopenharmony_ci MPIC_GREG_FEATURE_0, 738c2ecf20Sopenharmony_ci MPIC_GREG_GLOBAL_CONF_0, 748c2ecf20Sopenharmony_ci MPIC_GREG_VENDOR_ID, 758c2ecf20Sopenharmony_ci MPIC_GREG_IPI_VECTOR_PRI_0, 768c2ecf20Sopenharmony_ci MPIC_GREG_IPI_STRIDE, 778c2ecf20Sopenharmony_ci MPIC_GREG_SPURIOUS, 788c2ecf20Sopenharmony_ci MPIC_GREG_TIMER_FREQ, 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci MPIC_TIMER_BASE, 818c2ecf20Sopenharmony_ci MPIC_TIMER_STRIDE, 828c2ecf20Sopenharmony_ci MPIC_TIMER_CURRENT_CNT, 838c2ecf20Sopenharmony_ci MPIC_TIMER_BASE_CNT, 848c2ecf20Sopenharmony_ci MPIC_TIMER_VECTOR_PRI, 858c2ecf20Sopenharmony_ci MPIC_TIMER_DESTINATION, 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci MPIC_CPU_BASE, 888c2ecf20Sopenharmony_ci MPIC_CPU_STRIDE, 898c2ecf20Sopenharmony_ci MPIC_CPU_IPI_DISPATCH_0, 908c2ecf20Sopenharmony_ci MPIC_CPU_IPI_DISPATCH_STRIDE, 918c2ecf20Sopenharmony_ci MPIC_CPU_CURRENT_TASK_PRI, 928c2ecf20Sopenharmony_ci MPIC_CPU_WHOAMI, 938c2ecf20Sopenharmony_ci MPIC_CPU_INTACK, 948c2ecf20Sopenharmony_ci MPIC_CPU_EOI, 958c2ecf20Sopenharmony_ci MPIC_CPU_MCACK, 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci MPIC_IRQ_BASE, 988c2ecf20Sopenharmony_ci MPIC_IRQ_STRIDE, 998c2ecf20Sopenharmony_ci MPIC_IRQ_VECTOR_PRI, 1008c2ecf20Sopenharmony_ci MPIC_VECPRI_VECTOR_MASK, 1018c2ecf20Sopenharmony_ci MPIC_VECPRI_POLARITY_POSITIVE, 1028c2ecf20Sopenharmony_ci MPIC_VECPRI_POLARITY_NEGATIVE, 1038c2ecf20Sopenharmony_ci MPIC_VECPRI_SENSE_LEVEL, 1048c2ecf20Sopenharmony_ci MPIC_VECPRI_SENSE_EDGE, 1058c2ecf20Sopenharmony_ci MPIC_VECPRI_POLARITY_MASK, 1068c2ecf20Sopenharmony_ci MPIC_VECPRI_SENSE_MASK, 1078c2ecf20Sopenharmony_ci MPIC_IRQ_DESTINATION 1088c2ecf20Sopenharmony_ci }, 1098c2ecf20Sopenharmony_ci [1] = { /* Tsi108/109 PIC */ 1108c2ecf20Sopenharmony_ci TSI108_GREG_BASE, 1118c2ecf20Sopenharmony_ci TSI108_GREG_FEATURE_0, 1128c2ecf20Sopenharmony_ci TSI108_GREG_GLOBAL_CONF_0, 1138c2ecf20Sopenharmony_ci TSI108_GREG_VENDOR_ID, 1148c2ecf20Sopenharmony_ci TSI108_GREG_IPI_VECTOR_PRI_0, 1158c2ecf20Sopenharmony_ci TSI108_GREG_IPI_STRIDE, 1168c2ecf20Sopenharmony_ci TSI108_GREG_SPURIOUS, 1178c2ecf20Sopenharmony_ci TSI108_GREG_TIMER_FREQ, 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci TSI108_TIMER_BASE, 1208c2ecf20Sopenharmony_ci TSI108_TIMER_STRIDE, 1218c2ecf20Sopenharmony_ci TSI108_TIMER_CURRENT_CNT, 1228c2ecf20Sopenharmony_ci TSI108_TIMER_BASE_CNT, 1238c2ecf20Sopenharmony_ci TSI108_TIMER_VECTOR_PRI, 1248c2ecf20Sopenharmony_ci TSI108_TIMER_DESTINATION, 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci TSI108_CPU_BASE, 1278c2ecf20Sopenharmony_ci TSI108_CPU_STRIDE, 1288c2ecf20Sopenharmony_ci TSI108_CPU_IPI_DISPATCH_0, 1298c2ecf20Sopenharmony_ci TSI108_CPU_IPI_DISPATCH_STRIDE, 1308c2ecf20Sopenharmony_ci TSI108_CPU_CURRENT_TASK_PRI, 1318c2ecf20Sopenharmony_ci TSI108_CPU_WHOAMI, 1328c2ecf20Sopenharmony_ci TSI108_CPU_INTACK, 1338c2ecf20Sopenharmony_ci TSI108_CPU_EOI, 1348c2ecf20Sopenharmony_ci TSI108_CPU_MCACK, 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci TSI108_IRQ_BASE, 1378c2ecf20Sopenharmony_ci TSI108_IRQ_STRIDE, 1388c2ecf20Sopenharmony_ci TSI108_IRQ_VECTOR_PRI, 1398c2ecf20Sopenharmony_ci TSI108_VECPRI_VECTOR_MASK, 1408c2ecf20Sopenharmony_ci TSI108_VECPRI_POLARITY_POSITIVE, 1418c2ecf20Sopenharmony_ci TSI108_VECPRI_POLARITY_NEGATIVE, 1428c2ecf20Sopenharmony_ci TSI108_VECPRI_SENSE_LEVEL, 1438c2ecf20Sopenharmony_ci TSI108_VECPRI_SENSE_EDGE, 1448c2ecf20Sopenharmony_ci TSI108_VECPRI_POLARITY_MASK, 1458c2ecf20Sopenharmony_ci TSI108_VECPRI_SENSE_MASK, 1468c2ecf20Sopenharmony_ci TSI108_IRQ_DESTINATION 1478c2ecf20Sopenharmony_ci }, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name] 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#else /* CONFIG_MPIC_WEIRD */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define MPIC_INFO(name) MPIC_##name 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#endif /* CONFIG_MPIC_WEIRD */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic inline unsigned int mpic_processor_id(struct mpic *mpic) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci unsigned int cpu = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 1638c2ecf20Sopenharmony_ci cpu = hard_smp_processor_id(); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return cpu; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Register accessor functions 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic inline u32 _mpic_read(enum mpic_reg_type type, 1748c2ecf20Sopenharmony_ci struct mpic_reg_bank *rb, 1758c2ecf20Sopenharmony_ci unsigned int reg) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci switch(type) { 1788c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR 1798c2ecf20Sopenharmony_ci case mpic_access_dcr: 1808c2ecf20Sopenharmony_ci return dcr_read(rb->dhost, reg); 1818c2ecf20Sopenharmony_ci#endif 1828c2ecf20Sopenharmony_ci case mpic_access_mmio_be: 1838c2ecf20Sopenharmony_ci return in_be32(rb->base + (reg >> 2)); 1848c2ecf20Sopenharmony_ci case mpic_access_mmio_le: 1858c2ecf20Sopenharmony_ci default: 1868c2ecf20Sopenharmony_ci return in_le32(rb->base + (reg >> 2)); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline void _mpic_write(enum mpic_reg_type type, 1918c2ecf20Sopenharmony_ci struct mpic_reg_bank *rb, 1928c2ecf20Sopenharmony_ci unsigned int reg, u32 value) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci switch(type) { 1958c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR 1968c2ecf20Sopenharmony_ci case mpic_access_dcr: 1978c2ecf20Sopenharmony_ci dcr_write(rb->dhost, reg, value); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci#endif 2008c2ecf20Sopenharmony_ci case mpic_access_mmio_be: 2018c2ecf20Sopenharmony_ci out_be32(rb->base + (reg >> 2), value); 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case mpic_access_mmio_le: 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci out_le32(rb->base + (reg >> 2), value); 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci enum mpic_reg_type type = mpic->reg_type; 2138c2ecf20Sopenharmony_ci unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 2148c2ecf20Sopenharmony_ci (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le) 2178c2ecf20Sopenharmony_ci type = mpic_access_mmio_be; 2188c2ecf20Sopenharmony_ci return _mpic_read(type, &mpic->gregs, offset); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 2248c2ecf20Sopenharmony_ci (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE + 2328c2ecf20Sopenharmony_ci (tm & 3) * MPIC_INFO(TIMER_STRIDE); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, tm) + 2388c2ecf20Sopenharmony_ci MPIC_INFO(TIMER_VECTOR_PRI); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return _mpic_read(mpic->reg_type, &mpic->tmregs, offset); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, tm) + 2468c2ecf20Sopenharmony_ci MPIC_INFO(TIMER_VECTOR_PRI); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci unsigned int cpu = mpic_processor_id(mpic); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned int cpu = mpic_processor_id(mpic); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci unsigned int isu = src_no >> mpic->isu_shift; 2688c2ecf20Sopenharmony_ci unsigned int idx = src_no & mpic->isu_mask; 2698c2ecf20Sopenharmony_ci unsigned int val; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci val = _mpic_read(mpic->reg_type, &mpic->isus[isu], 2728c2ecf20Sopenharmony_ci reg + (idx * MPIC_INFO(IRQ_STRIDE))); 2738c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_BROKEN_REGREAD 2748c2ecf20Sopenharmony_ci if (reg == 0) 2758c2ecf20Sopenharmony_ci val = (val & (MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY)) | 2768c2ecf20Sopenharmony_ci mpic->isu_reg0_shadow[src_no]; 2778c2ecf20Sopenharmony_ci#endif 2788c2ecf20Sopenharmony_ci return val; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, 2828c2ecf20Sopenharmony_ci unsigned int reg, u32 value) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci unsigned int isu = src_no >> mpic->isu_shift; 2858c2ecf20Sopenharmony_ci unsigned int idx = src_no & mpic->isu_mask; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->isus[isu], 2888c2ecf20Sopenharmony_ci reg + (idx * MPIC_INFO(IRQ_STRIDE)), value); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_BROKEN_REGREAD 2918c2ecf20Sopenharmony_ci if (reg == 0) 2928c2ecf20Sopenharmony_ci mpic->isu_reg0_shadow[src_no] = 2938c2ecf20Sopenharmony_ci value & ~(MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY); 2948c2ecf20Sopenharmony_ci#endif 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define mpic_read(b,r) _mpic_read(mpic->reg_type,&(b),(r)) 2988c2ecf20Sopenharmony_ci#define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) 2998c2ecf20Sopenharmony_ci#define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) 3008c2ecf20Sopenharmony_ci#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) 3018c2ecf20Sopenharmony_ci#define mpic_tm_read(i) _mpic_tm_read(mpic,(i)) 3028c2ecf20Sopenharmony_ci#define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v)) 3038c2ecf20Sopenharmony_ci#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) 3048c2ecf20Sopenharmony_ci#define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) 3058c2ecf20Sopenharmony_ci#define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) 3068c2ecf20Sopenharmony_ci#define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v)) 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* 3108c2ecf20Sopenharmony_ci * Low level utility functions 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr, 3158c2ecf20Sopenharmony_ci struct mpic_reg_bank *rb, unsigned int offset, 3168c2ecf20Sopenharmony_ci unsigned int size) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci rb->base = ioremap(phys_addr + offset, size); 3198c2ecf20Sopenharmony_ci BUG_ON(rb->base == NULL); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR 3238c2ecf20Sopenharmony_cistatic void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, 3248c2ecf20Sopenharmony_ci unsigned int offset, unsigned int size) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0); 3278c2ecf20Sopenharmony_ci rb->dhost = dcr_map(mpic->node, phys_addr + offset, size); 3288c2ecf20Sopenharmony_ci BUG_ON(!DCR_MAP_OK(rb->dhost)); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic inline void mpic_map(struct mpic *mpic, 3328c2ecf20Sopenharmony_ci phys_addr_t phys_addr, struct mpic_reg_bank *rb, 3338c2ecf20Sopenharmony_ci unsigned int offset, unsigned int size) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_USES_DCR) 3368c2ecf20Sopenharmony_ci _mpic_map_dcr(mpic, rb, offset, size); 3378c2ecf20Sopenharmony_ci else 3388c2ecf20Sopenharmony_ci _mpic_map_mmio(mpic, phys_addr, rb, offset, size); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci#else /* CONFIG_PPC_DCR */ 3418c2ecf20Sopenharmony_ci#define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) 3428c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC_DCR */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* Check if we have one of those nice broken MPICs with a flipped endian on 3478c2ecf20Sopenharmony_ci * reads from IPI registers 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic void __init mpic_test_broken_ipi(struct mpic *mpic) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci u32 r; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK); 3548c2ecf20Sopenharmony_ci r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0)); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (r == le32_to_cpu(MPIC_VECPRI_MASK)) { 3578c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); 3588c2ecf20Sopenharmony_ci mpic->flags |= MPIC_BROKEN_IPI; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* Test if an interrupt is sourced from HyperTransport (used on broken U3s) 3658c2ecf20Sopenharmony_ci * to force the edge setting on the MPIC and do the ack workaround. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci if (source >= 128 || !mpic->fixups) 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci return mpic->fixups[source].base != NULL; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (fixup->applebase) { 3808c2ecf20Sopenharmony_ci unsigned int soff = (fixup->index >> 3) & ~3; 3818c2ecf20Sopenharmony_ci unsigned int mask = 1U << (fixup->index & 0x1f); 3828c2ecf20Sopenharmony_ci writel(mask, fixup->applebase + soff); 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci raw_spin_lock(&mpic->fixup_lock); 3858c2ecf20Sopenharmony_ci writeb(0x11 + 2 * fixup->index, fixup->base + 2); 3868c2ecf20Sopenharmony_ci writel(fixup->data, fixup->base + 4); 3878c2ecf20Sopenharmony_ci raw_spin_unlock(&mpic->fixup_lock); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, 3928c2ecf20Sopenharmony_ci bool level) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 3958c2ecf20Sopenharmony_ci unsigned long flags; 3968c2ecf20Sopenharmony_ci u32 tmp; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (fixup->base == NULL) 3998c2ecf20Sopenharmony_ci return; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci DBG("startup_ht_interrupt(0x%x) index: %d\n", 4028c2ecf20Sopenharmony_ci source, fixup->index); 4038c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 4048c2ecf20Sopenharmony_ci /* Enable and configure */ 4058c2ecf20Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 4068c2ecf20Sopenharmony_ci tmp = readl(fixup->base + 4); 4078c2ecf20Sopenharmony_ci tmp &= ~(0x23U); 4088c2ecf20Sopenharmony_ci if (level) 4098c2ecf20Sopenharmony_ci tmp |= 0x22; 4108c2ecf20Sopenharmony_ci writel(tmp, fixup->base + 4); 4118c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4148c2ecf20Sopenharmony_ci /* use the lowest bit inverted to the actual HW, 4158c2ecf20Sopenharmony_ci * set if this fixup was enabled, clear otherwise */ 4168c2ecf20Sopenharmony_ci mpic->save_data[source].fixup_data = tmp | 1; 4178c2ecf20Sopenharmony_ci#endif 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 4238c2ecf20Sopenharmony_ci unsigned long flags; 4248c2ecf20Sopenharmony_ci u32 tmp; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (fixup->base == NULL) 4278c2ecf20Sopenharmony_ci return; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci DBG("shutdown_ht_interrupt(0x%x)\n", source); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Disable */ 4328c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 4338c2ecf20Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 4348c2ecf20Sopenharmony_ci tmp = readl(fixup->base + 4); 4358c2ecf20Sopenharmony_ci tmp |= 1; 4368c2ecf20Sopenharmony_ci writel(tmp, fixup->base + 4); 4378c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4408c2ecf20Sopenharmony_ci /* use the lowest bit inverted to the actual HW, 4418c2ecf20Sopenharmony_ci * set if this fixup was enabled, clear otherwise */ 4428c2ecf20Sopenharmony_ci mpic->save_data[source].fixup_data = tmp & ~1; 4438c2ecf20Sopenharmony_ci#endif 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 4478c2ecf20Sopenharmony_cistatic void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 4488c2ecf20Sopenharmony_ci unsigned int devfn) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci u8 __iomem *base; 4518c2ecf20Sopenharmony_ci u8 pos, flags; 4528c2ecf20Sopenharmony_ci u64 addr = 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 4558c2ecf20Sopenharmony_ci pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 4568c2ecf20Sopenharmony_ci u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 4578c2ecf20Sopenharmony_ci if (id == PCI_CAP_ID_HT) { 4588c2ecf20Sopenharmony_ci id = readb(devbase + pos + 3); 4598c2ecf20Sopenharmony_ci if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING) 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (pos == 0) 4658c2ecf20Sopenharmony_ci return; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci base = devbase + pos; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci flags = readb(base + HT_MSI_FLAGS); 4708c2ecf20Sopenharmony_ci if (!(flags & HT_MSI_FLAGS_FIXED)) { 4718c2ecf20Sopenharmony_ci addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK; 4728c2ecf20Sopenharmony_ci addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%llx\n", 4768c2ecf20Sopenharmony_ci PCI_SLOT(devfn), PCI_FUNC(devfn), 4778c2ecf20Sopenharmony_ci flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!(flags & HT_MSI_FLAGS_ENABLE)) 4808c2ecf20Sopenharmony_ci writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci#else 4838c2ecf20Sopenharmony_cistatic void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 4848c2ecf20Sopenharmony_ci unsigned int devfn) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci return; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci#endif 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, 4918c2ecf20Sopenharmony_ci unsigned int devfn, u32 vdid) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci int i, irq, n; 4948c2ecf20Sopenharmony_ci u8 __iomem *base; 4958c2ecf20Sopenharmony_ci u32 tmp; 4968c2ecf20Sopenharmony_ci u8 pos; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 4998c2ecf20Sopenharmony_ci pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 5008c2ecf20Sopenharmony_ci u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 5018c2ecf20Sopenharmony_ci if (id == PCI_CAP_ID_HT) { 5028c2ecf20Sopenharmony_ci id = readb(devbase + pos + 3); 5038c2ecf20Sopenharmony_ci if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_IRQ) 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci if (pos == 0) 5088c2ecf20Sopenharmony_ci return; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci base = devbase + pos; 5118c2ecf20Sopenharmony_ci writeb(0x01, base + 2); 5128c2ecf20Sopenharmony_ci n = (readl(base + 4) >> 16) & 0xff; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" 5158c2ecf20Sopenharmony_ci " has %d irqs\n", 5168c2ecf20Sopenharmony_ci devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for (i = 0; i <= n; i++) { 5198c2ecf20Sopenharmony_ci writeb(0x10 + 2 * i, base + 2); 5208c2ecf20Sopenharmony_ci tmp = readl(base + 4); 5218c2ecf20Sopenharmony_ci irq = (tmp >> 16) & 0xff; 5228c2ecf20Sopenharmony_ci DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); 5238c2ecf20Sopenharmony_ci /* mask it , will be unmasked later */ 5248c2ecf20Sopenharmony_ci tmp |= 0x1; 5258c2ecf20Sopenharmony_ci writel(tmp, base + 4); 5268c2ecf20Sopenharmony_ci mpic->fixups[irq].index = i; 5278c2ecf20Sopenharmony_ci mpic->fixups[irq].base = base; 5288c2ecf20Sopenharmony_ci /* Apple HT PIC has a non-standard way of doing EOIs */ 5298c2ecf20Sopenharmony_ci if ((vdid & 0xffff) == 0x106b) 5308c2ecf20Sopenharmony_ci mpic->fixups[irq].applebase = devbase + 0x60; 5318c2ecf20Sopenharmony_ci else 5328c2ecf20Sopenharmony_ci mpic->fixups[irq].applebase = NULL; 5338c2ecf20Sopenharmony_ci writeb(0x11 + 2 * i, base + 2); 5348c2ecf20Sopenharmony_ci mpic->fixups[irq].data = readl(base + 4) | 0x80000000; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void __init mpic_scan_ht_pics(struct mpic *mpic) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned int devfn; 5428c2ecf20Sopenharmony_ci u8 __iomem *cfgspace; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Allocate fixups array */ 5478c2ecf20Sopenharmony_ci mpic->fixups = kcalloc(128, sizeof(*mpic->fixups), GFP_KERNEL); 5488c2ecf20Sopenharmony_ci BUG_ON(mpic->fixups == NULL); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Init spinlock */ 5518c2ecf20Sopenharmony_ci raw_spin_lock_init(&mpic->fixup_lock); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Map U3 config space. We assume all IO-APICs are on the primary bus 5548c2ecf20Sopenharmony_ci * so we only need to map 64kB. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci cfgspace = ioremap(0xf2000000, 0x10000); 5578c2ecf20Sopenharmony_ci BUG_ON(cfgspace == NULL); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Now we scan all slots. We do a very quick scan, we read the header 5608c2ecf20Sopenharmony_ci * type, vendor ID and device ID only, that's plenty enough 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci for (devfn = 0; devfn < 0x100; devfn++) { 5638c2ecf20Sopenharmony_ci u8 __iomem *devbase = cfgspace + (devfn << 8); 5648c2ecf20Sopenharmony_ci u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); 5658c2ecf20Sopenharmony_ci u32 l = readl(devbase + PCI_VENDOR_ID); 5668c2ecf20Sopenharmony_ci u16 s; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci DBG("devfn %x, l: %x\n", devfn, l); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* If no device, skip */ 5718c2ecf20Sopenharmony_ci if (l == 0xffffffff || l == 0x00000000 || 5728c2ecf20Sopenharmony_ci l == 0x0000ffff || l == 0xffff0000) 5738c2ecf20Sopenharmony_ci goto next; 5748c2ecf20Sopenharmony_ci /* Check if is supports capability lists */ 5758c2ecf20Sopenharmony_ci s = readw(devbase + PCI_STATUS); 5768c2ecf20Sopenharmony_ci if (!(s & PCI_STATUS_CAP_LIST)) 5778c2ecf20Sopenharmony_ci goto next; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci mpic_scan_ht_pic(mpic, devbase, devfn, l); 5808c2ecf20Sopenharmony_ci mpic_scan_ht_msi(mpic, devbase, devfn); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci next: 5838c2ecf20Sopenharmony_ci /* next device, if function 0 */ 5848c2ecf20Sopenharmony_ci if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) 5858c2ecf20Sopenharmony_ci devfn += 7; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci#else /* CONFIG_MPIC_U3_HT_IRQS */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic void __init mpic_scan_ht_pics(struct mpic *mpic) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci/* Find an mpic associated with a given linux interrupt */ 6038c2ecf20Sopenharmony_cistatic struct mpic *mpic_find(unsigned int irq) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci if (irq < NUM_ISA_INTERRUPTS) 6068c2ecf20Sopenharmony_ci return NULL; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return irq_get_chip_data(irq); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* Determine if the linux irq is an IPI */ 6128c2ecf20Sopenharmony_cistatic unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/* Determine if the linux irq is a timer */ 6188c2ecf20Sopenharmony_cistatic unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/* Convert a cpu mask from logical to physical cpu numbers. */ 6248c2ecf20Sopenharmony_cistatic inline u32 mpic_physmask(u32 cpumask) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int i; 6278c2ecf20Sopenharmony_ci u32 mask = 0; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci for (i = 0; i < min(32, NR_CPUS) && cpu_possible(i); ++i, cpumask >>= 1) 6308c2ecf20Sopenharmony_ci mask |= (cpumask & 1) << get_hard_smp_processor_id(i); 6318c2ecf20Sopenharmony_ci return mask; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 6358c2ecf20Sopenharmony_ci/* Get the mpic structure from the IPI number */ 6368c2ecf20Sopenharmony_cistatic inline struct mpic * mpic_from_ipi(struct irq_data *d) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci return irq_data_get_irq_chip_data(d); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci#endif 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* Get the mpic structure from the irq number */ 6438c2ecf20Sopenharmony_cistatic inline struct mpic * mpic_from_irq(unsigned int irq) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci return irq_get_chip_data(irq); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* Get the mpic structure from the irq data */ 6498c2ecf20Sopenharmony_cistatic inline struct mpic * mpic_from_irq_data(struct irq_data *d) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci return irq_data_get_irq_chip_data(d); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* Send an EOI */ 6558c2ecf20Sopenharmony_cistatic inline void mpic_eoi(struct mpic *mpic) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_EOI), 0); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* 6618c2ecf20Sopenharmony_ci * Linux descriptor level callbacks 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_civoid mpic_unmask_irq(struct irq_data *d) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci unsigned int loops = 100000; 6688c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 6698c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6748c2ecf20Sopenharmony_ci mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & 6758c2ecf20Sopenharmony_ci ~MPIC_VECPRI_MASK); 6768c2ecf20Sopenharmony_ci /* make sure mask gets to controller before we return to user */ 6778c2ecf20Sopenharmony_ci do { 6788c2ecf20Sopenharmony_ci if (!loops--) { 6798c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: timeout on hwirq %u\n", 6808c2ecf20Sopenharmony_ci __func__, src); 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_civoid mpic_mask_irq(struct irq_data *d) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci unsigned int loops = 100000; 6898c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 6908c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6958c2ecf20Sopenharmony_ci mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | 6968c2ecf20Sopenharmony_ci MPIC_VECPRI_MASK); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* make sure mask gets to controller before we return to user */ 6998c2ecf20Sopenharmony_ci do { 7008c2ecf20Sopenharmony_ci if (!loops--) { 7018c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: timeout on hwirq %u\n", 7028c2ecf20Sopenharmony_ci __func__, src); 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_civoid mpic_end_irq(struct irq_data *d) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQ 7138c2ecf20Sopenharmony_ci DBG("%s: end_irq: %d\n", mpic->name, d->irq); 7148c2ecf20Sopenharmony_ci#endif 7158c2ecf20Sopenharmony_ci /* We always EOI on end_irq() even for edge interrupts since that 7168c2ecf20Sopenharmony_ci * should only lower the priority, the MPIC should have properly 7178c2ecf20Sopenharmony_ci * latched another edge interrupt coming in anyway 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci mpic_eoi(mpic); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic void mpic_unmask_ht_irq(struct irq_data *d) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 7288c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci mpic_unmask_irq(d); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (irqd_is_level_type(d)) 7338c2ecf20Sopenharmony_ci mpic_ht_end_irq(mpic, src); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic unsigned int mpic_startup_ht_irq(struct irq_data *d) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 7398c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci mpic_unmask_irq(d); 7428c2ecf20Sopenharmony_ci mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d)); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic void mpic_shutdown_ht_irq(struct irq_data *d) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 7508c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci mpic_shutdown_ht_interrupt(mpic, src); 7538c2ecf20Sopenharmony_ci mpic_mask_irq(d); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic void mpic_end_ht_irq(struct irq_data *d) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 7598c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQ 7628c2ecf20Sopenharmony_ci DBG("%s: end_irq: %d\n", mpic->name, d->irq); 7638c2ecf20Sopenharmony_ci#endif 7648c2ecf20Sopenharmony_ci /* We always EOI on end_irq() even for edge interrupts since that 7658c2ecf20Sopenharmony_ci * should only lower the priority, the MPIC should have properly 7668c2ecf20Sopenharmony_ci * latched another edge interrupt coming in anyway 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (irqd_is_level_type(d)) 7708c2ecf20Sopenharmony_ci mpic_ht_end_irq(mpic, src); 7718c2ecf20Sopenharmony_ci mpic_eoi(mpic); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci#endif /* !CONFIG_MPIC_U3_HT_IRQS */ 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void mpic_unmask_ipi(struct irq_data *d) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_ipi(d); 7808c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0]; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src); 7838c2ecf20Sopenharmony_ci mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic void mpic_mask_ipi(struct irq_data *d) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci /* NEVER disable an IPI... that's just plain wrong! */ 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic void mpic_end_ipi(struct irq_data *d) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_ipi(d); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * IPIs are marked IRQ_PER_CPU. This has the side effect of 7978c2ecf20Sopenharmony_ci * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from 7988c2ecf20Sopenharmony_ci * applying to them. We EOI them late to avoid re-entering. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci mpic_eoi(mpic); 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic void mpic_unmask_tm(struct irq_data *d) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 8088c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src); 8118c2ecf20Sopenharmony_ci mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK); 8128c2ecf20Sopenharmony_ci mpic_tm_read(src); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void mpic_mask_tm(struct irq_data *d) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 8188c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK); 8218c2ecf20Sopenharmony_ci mpic_tm_read(src); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ciint mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, 8258c2ecf20Sopenharmony_ci bool force) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 8288c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_SINGLE_DEST_CPU) { 8318c2ecf20Sopenharmony_ci int cpuid = irq_choose_cpu(cpumask); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 8348c2ecf20Sopenharmony_ci } else { 8358c2ecf20Sopenharmony_ci u32 mask = cpumask_bits(cpumask)[0]; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci mask &= cpumask_bits(cpu_online_mask)[0]; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 8408c2ecf20Sopenharmony_ci mpic_physmask(mask)); 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_cistatic unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci /* Now convert sense value */ 8498c2ecf20Sopenharmony_ci switch(type & IRQ_TYPE_SENSE_MASK) { 8508c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 8518c2ecf20Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_EDGE) | 8528c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE); 8538c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 8548c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 8558c2ecf20Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_EDGE) | 8568c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 8578c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 8588c2ecf20Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_LEVEL) | 8598c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE); 8608c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 8618c2ecf20Sopenharmony_ci default: 8628c2ecf20Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_LEVEL) | 8638c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ciint mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 8708c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 8718c2ecf20Sopenharmony_ci unsigned int vecpri, vold, vnew; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", 8748c2ecf20Sopenharmony_ci mpic, d->irq, src, flow_type); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (src >= mpic->num_sources) 8778c2ecf20Sopenharmony_ci return -EINVAL; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* We don't support "none" type */ 8828c2ecf20Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 8838c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_DEFAULT; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* Default: read HW settings */ 8868c2ecf20Sopenharmony_ci if (flow_type == IRQ_TYPE_DEFAULT) { 8878c2ecf20Sopenharmony_ci int vold_ps; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci vold_ps = vold & (MPIC_INFO(VECPRI_POLARITY_MASK) | 8908c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_SENSE_MASK)); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | 8938c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE))) 8948c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_EDGE_RISING; 8958c2ecf20Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | 8968c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) 8978c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_EDGE_FALLING; 8988c2ecf20Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | 8998c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE))) 9008c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_HIGH; 9018c2ecf20Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | 9028c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) 9038c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 9048c2ecf20Sopenharmony_ci else 9058c2ecf20Sopenharmony_ci WARN_ONCE(1, "mpic: unknown IRQ type %d\n", vold); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* Apply to irq desc */ 9098c2ecf20Sopenharmony_ci irqd_set_trigger_type(d, flow_type); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* Apply to HW */ 9128c2ecf20Sopenharmony_ci if (mpic_is_ht_interrupt(mpic, src)) 9138c2ecf20Sopenharmony_ci vecpri = MPIC_VECPRI_POLARITY_POSITIVE | 9148c2ecf20Sopenharmony_ci MPIC_VECPRI_SENSE_EDGE; 9158c2ecf20Sopenharmony_ci else 9168c2ecf20Sopenharmony_ci vecpri = mpic_type_to_vecpri(mpic, flow_type); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | 9198c2ecf20Sopenharmony_ci MPIC_INFO(VECPRI_SENSE_MASK)); 9208c2ecf20Sopenharmony_ci vnew |= vecpri; 9218c2ecf20Sopenharmony_ci if (vold != vnew) 9228c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK_NOCOPY; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_civoid mpic_set_vector(unsigned int virq, unsigned int vector) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq(virq); 9308c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(virq); 9318c2ecf20Sopenharmony_ci unsigned int vecpri; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", 9348c2ecf20Sopenharmony_ci mpic, virq, src, vector); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (src >= mpic->num_sources) 9378c2ecf20Sopenharmony_ci return; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 9408c2ecf20Sopenharmony_ci vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK); 9418c2ecf20Sopenharmony_ci vecpri |= vector; 9428c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cistatic void mpic_set_destination(unsigned int virq, unsigned int cpuid) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_from_irq(virq); 9488c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(virq); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n", 9518c2ecf20Sopenharmony_ci mpic, virq, src, cpuid); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (src >= mpic->num_sources) 9548c2ecf20Sopenharmony_ci return; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic struct irq_chip mpic_irq_chip = { 9608c2ecf20Sopenharmony_ci .irq_mask = mpic_mask_irq, 9618c2ecf20Sopenharmony_ci .irq_unmask = mpic_unmask_irq, 9628c2ecf20Sopenharmony_ci .irq_eoi = mpic_end_irq, 9638c2ecf20Sopenharmony_ci .irq_set_type = mpic_set_irq_type, 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 9678c2ecf20Sopenharmony_cistatic const struct irq_chip mpic_ipi_chip = { 9688c2ecf20Sopenharmony_ci .irq_mask = mpic_mask_ipi, 9698c2ecf20Sopenharmony_ci .irq_unmask = mpic_unmask_ipi, 9708c2ecf20Sopenharmony_ci .irq_eoi = mpic_end_ipi, 9718c2ecf20Sopenharmony_ci}; 9728c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic struct irq_chip mpic_tm_chip = { 9758c2ecf20Sopenharmony_ci .irq_mask = mpic_mask_tm, 9768c2ecf20Sopenharmony_ci .irq_unmask = mpic_unmask_tm, 9778c2ecf20Sopenharmony_ci .irq_eoi = mpic_end_irq, 9788c2ecf20Sopenharmony_ci}; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 9818c2ecf20Sopenharmony_cistatic const struct irq_chip mpic_irq_ht_chip = { 9828c2ecf20Sopenharmony_ci .irq_startup = mpic_startup_ht_irq, 9838c2ecf20Sopenharmony_ci .irq_shutdown = mpic_shutdown_ht_irq, 9848c2ecf20Sopenharmony_ci .irq_mask = mpic_mask_irq, 9858c2ecf20Sopenharmony_ci .irq_unmask = mpic_unmask_ht_irq, 9868c2ecf20Sopenharmony_ci .irq_eoi = mpic_end_ht_irq, 9878c2ecf20Sopenharmony_ci .irq_set_type = mpic_set_irq_type, 9888c2ecf20Sopenharmony_ci}; 9898c2ecf20Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic int mpic_host_match(struct irq_domain *h, struct device_node *node, 9938c2ecf20Sopenharmony_ci enum irq_domain_bus_token bus_token) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci /* Exact match, unless mpic node is NULL */ 9968c2ecf20Sopenharmony_ci struct device_node *of_node = irq_domain_get_of_node(h); 9978c2ecf20Sopenharmony_ci return of_node == NULL || of_node == node; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int mpic_host_map(struct irq_domain *h, unsigned int virq, 10018c2ecf20Sopenharmony_ci irq_hw_number_t hw) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct mpic *mpic = h->host_data; 10048c2ecf20Sopenharmony_ci struct irq_chip *chip; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (hw == mpic->spurious_vec) 10098c2ecf20Sopenharmony_ci return -EINVAL; 10108c2ecf20Sopenharmony_ci if (mpic->protected && test_bit(hw, mpic->protected)) { 10118c2ecf20Sopenharmony_ci pr_warn("mpic: Mapping of source 0x%x failed, source protected by firmware !\n", 10128c2ecf20Sopenharmony_ci (unsigned int)hw); 10138c2ecf20Sopenharmony_ci return -EPERM; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 10178c2ecf20Sopenharmony_ci else if (hw >= mpic->ipi_vecs[0]) { 10188c2ecf20Sopenharmony_ci WARN_ON(mpic->flags & MPIC_SECONDARY); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci DBG("mpic: mapping as IPI\n"); 10218c2ecf20Sopenharmony_ci irq_set_chip_data(virq, mpic); 10228c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &mpic->hc_ipi, 10238c2ecf20Sopenharmony_ci handle_percpu_irq); 10248c2ecf20Sopenharmony_ci return 0; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { 10298c2ecf20Sopenharmony_ci WARN_ON(mpic->flags & MPIC_SECONDARY); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci DBG("mpic: mapping as timer\n"); 10328c2ecf20Sopenharmony_ci irq_set_chip_data(virq, mpic); 10338c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &mpic->hc_tm, 10348c2ecf20Sopenharmony_ci handle_fasteoi_irq); 10358c2ecf20Sopenharmony_ci return 0; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (mpic_map_error_int(mpic, virq, hw)) 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (hw >= mpic->num_sources) { 10428c2ecf20Sopenharmony_ci pr_warn("mpic: Mapping of source 0x%x failed, source out of range !\n", 10438c2ecf20Sopenharmony_ci (unsigned int)hw); 10448c2ecf20Sopenharmony_ci return -EINVAL; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci mpic_msi_reserve_hwirq(mpic, hw); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* Default chip */ 10508c2ecf20Sopenharmony_ci chip = &mpic->hc_irq; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 10538c2ecf20Sopenharmony_ci /* Check for HT interrupts, override vecpri */ 10548c2ecf20Sopenharmony_ci if (mpic_is_ht_interrupt(mpic, hw)) 10558c2ecf20Sopenharmony_ci chip = &mpic->hc_ht_irq; 10568c2ecf20Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci DBG("mpic: mapping to irq chip @%p\n", chip); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci irq_set_chip_data(virq, mpic); 10618c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* Set default irq type */ 10648c2ecf20Sopenharmony_ci irq_set_irq_type(virq, IRQ_TYPE_DEFAULT); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* If the MPIC was reset, then all vectors have already been 10678c2ecf20Sopenharmony_ci * initialized. Otherwise, a per source lazy initialization 10688c2ecf20Sopenharmony_ci * is done here. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) { 10718c2ecf20Sopenharmony_ci int cpu; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci preempt_disable(); 10748c2ecf20Sopenharmony_ci cpu = mpic_processor_id(mpic); 10758c2ecf20Sopenharmony_ci preempt_enable(); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci mpic_set_vector(virq, hw); 10788c2ecf20Sopenharmony_ci mpic_set_destination(virq, cpu); 10798c2ecf20Sopenharmony_ci mpic_irq_set_priority(virq, 8); 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic int mpic_host_xlate(struct irq_domain *h, struct device_node *ct, 10868c2ecf20Sopenharmony_ci const u32 *intspec, unsigned int intsize, 10878c2ecf20Sopenharmony_ci irq_hw_number_t *out_hwirq, unsigned int *out_flags) 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct mpic *mpic = h->host_data; 10918c2ecf20Sopenharmony_ci static unsigned char map_mpic_senses[4] = { 10928c2ecf20Sopenharmony_ci IRQ_TYPE_EDGE_RISING, 10938c2ecf20Sopenharmony_ci IRQ_TYPE_LEVEL_LOW, 10948c2ecf20Sopenharmony_ci IRQ_TYPE_LEVEL_HIGH, 10958c2ecf20Sopenharmony_ci IRQ_TYPE_EDGE_FALLING, 10968c2ecf20Sopenharmony_ci }; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci *out_hwirq = intspec[0]; 10998c2ecf20Sopenharmony_ci if (intsize >= 4 && (mpic->flags & MPIC_FSL)) { 11008c2ecf20Sopenharmony_ci /* 11018c2ecf20Sopenharmony_ci * Freescale MPIC with extended intspec: 11028c2ecf20Sopenharmony_ci * First two cells are as usual. Third specifies 11038c2ecf20Sopenharmony_ci * an "interrupt type". Fourth is type-specific data. 11048c2ecf20Sopenharmony_ci * 11058c2ecf20Sopenharmony_ci * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci switch (intspec[2]) { 11088c2ecf20Sopenharmony_ci case 0: 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case 1: 11118c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_FSL_HAS_EIMR)) 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs)) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci *out_hwirq = mpic->err_int_vecs[intspec[3]]; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case 2: 11218c2ecf20Sopenharmony_ci if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) 11228c2ecf20Sopenharmony_ci return -EINVAL; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci *out_hwirq = mpic->ipi_vecs[intspec[0]]; 11258c2ecf20Sopenharmony_ci break; 11268c2ecf20Sopenharmony_ci case 3: 11278c2ecf20Sopenharmony_ci if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs)) 11288c2ecf20Sopenharmony_ci return -EINVAL; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci *out_hwirq = mpic->timer_vecs[intspec[0]]; 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci default: 11338c2ecf20Sopenharmony_ci pr_debug("%s: unknown irq type %u\n", 11348c2ecf20Sopenharmony_ci __func__, intspec[2]); 11358c2ecf20Sopenharmony_ci return -EINVAL; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci *out_flags = map_mpic_senses[intspec[1] & 3]; 11398c2ecf20Sopenharmony_ci } else if (intsize > 1) { 11408c2ecf20Sopenharmony_ci u32 mask = 0x3; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* Apple invented a new race of encoding on machines with 11438c2ecf20Sopenharmony_ci * an HT APIC. They encode, among others, the index within 11448c2ecf20Sopenharmony_ci * the HT APIC. We don't care about it here since thankfully, 11458c2ecf20Sopenharmony_ci * it appears that they have the APIC already properly 11468c2ecf20Sopenharmony_ci * configured, and thus our current fixup code that reads the 11478c2ecf20Sopenharmony_ci * APIC config works fine. However, we still need to mask out 11488c2ecf20Sopenharmony_ci * bits in the specifier to make sure we only get bit 0 which 11498c2ecf20Sopenharmony_ci * is the level/edge bit (the only sense bit exposed by Apple), 11508c2ecf20Sopenharmony_ci * as their bit 1 means something else. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci if (machine_is(powermac)) 11538c2ecf20Sopenharmony_ci mask = 0x1; 11548c2ecf20Sopenharmony_ci *out_flags = map_mpic_senses[intspec[1] & mask]; 11558c2ecf20Sopenharmony_ci } else 11568c2ecf20Sopenharmony_ci *out_flags = IRQ_TYPE_NONE; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n", 11598c2ecf20Sopenharmony_ci intsize, intspec[0], intspec[1], *out_hwirq, *out_flags); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* IRQ handler for a secondary MPIC cascaded from another IRQ controller */ 11658c2ecf20Sopenharmony_cistatic void mpic_cascade(struct irq_desc *desc) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 11688c2ecf20Sopenharmony_ci struct mpic *mpic = irq_desc_get_handler_data(desc); 11698c2ecf20Sopenharmony_ci unsigned int virq; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci BUG_ON(!(mpic->flags & MPIC_SECONDARY)); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci virq = mpic_get_one_irq(mpic); 11748c2ecf20Sopenharmony_ci if (virq) 11758c2ecf20Sopenharmony_ci generic_handle_irq(virq); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci chip->irq_eoi(&desc->irq_data); 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic const struct irq_domain_ops mpic_host_ops = { 11818c2ecf20Sopenharmony_ci .match = mpic_host_match, 11828c2ecf20Sopenharmony_ci .map = mpic_host_map, 11838c2ecf20Sopenharmony_ci .xlate = mpic_host_xlate, 11848c2ecf20Sopenharmony_ci}; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic u32 fsl_mpic_get_version(struct mpic *mpic) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci u32 brr1; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_FSL)) 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, 11948c2ecf20Sopenharmony_ci MPIC_FSL_BRR1); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return brr1 & MPIC_FSL_BRR1_VER; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/* 12008c2ecf20Sopenharmony_ci * Exported functions 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ciu32 fsl_mpic_primary_get_version(void) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (mpic) 12088c2ecf20Sopenharmony_ci return fsl_mpic_get_version(mpic); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistruct mpic * __init mpic_alloc(struct device_node *node, 12148c2ecf20Sopenharmony_ci phys_addr_t phys_addr, 12158c2ecf20Sopenharmony_ci unsigned int flags, 12168c2ecf20Sopenharmony_ci unsigned int isu_size, 12178c2ecf20Sopenharmony_ci unsigned int irq_count, 12188c2ecf20Sopenharmony_ci const char *name) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci int i, psize, intvec_top; 12218c2ecf20Sopenharmony_ci struct mpic *mpic; 12228c2ecf20Sopenharmony_ci u32 greg_feature; 12238c2ecf20Sopenharmony_ci const char *vers; 12248c2ecf20Sopenharmony_ci const u32 *psrc; 12258c2ecf20Sopenharmony_ci u32 last_irq; 12268c2ecf20Sopenharmony_ci u32 fsl_version = 0; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* Default MPIC search parameters */ 12298c2ecf20Sopenharmony_ci static const struct of_device_id __initconst mpic_device_id[] = { 12308c2ecf20Sopenharmony_ci { .type = "open-pic", }, 12318c2ecf20Sopenharmony_ci { .compatible = "open-pic", }, 12328c2ecf20Sopenharmony_ci {}, 12338c2ecf20Sopenharmony_ci }; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* 12368c2ecf20Sopenharmony_ci * If we were not passed a device-tree node, then perform the default 12378c2ecf20Sopenharmony_ci * search for standardized a standardized OpenPIC. 12388c2ecf20Sopenharmony_ci */ 12398c2ecf20Sopenharmony_ci if (node) { 12408c2ecf20Sopenharmony_ci node = of_node_get(node); 12418c2ecf20Sopenharmony_ci } else { 12428c2ecf20Sopenharmony_ci node = of_find_matching_node(NULL, mpic_device_id); 12438c2ecf20Sopenharmony_ci if (!node) 12448c2ecf20Sopenharmony_ci return NULL; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* Pick the physical address from the device tree if unspecified */ 12488c2ecf20Sopenharmony_ci if (!phys_addr) { 12498c2ecf20Sopenharmony_ci /* Check if it is DCR-based */ 12508c2ecf20Sopenharmony_ci if (of_property_read_bool(node, "dcr-reg")) { 12518c2ecf20Sopenharmony_ci flags |= MPIC_USES_DCR; 12528c2ecf20Sopenharmony_ci } else { 12538c2ecf20Sopenharmony_ci struct resource r; 12548c2ecf20Sopenharmony_ci if (of_address_to_resource(node, 0, &r)) 12558c2ecf20Sopenharmony_ci goto err_of_node_put; 12568c2ecf20Sopenharmony_ci phys_addr = r.start; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* Read extra device-tree properties into the flags variable */ 12618c2ecf20Sopenharmony_ci if (of_get_property(node, "big-endian", NULL)) 12628c2ecf20Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 12638c2ecf20Sopenharmony_ci if (of_get_property(node, "pic-no-reset", NULL)) 12648c2ecf20Sopenharmony_ci flags |= MPIC_NO_RESET; 12658c2ecf20Sopenharmony_ci if (of_get_property(node, "single-cpu-affinity", NULL)) 12668c2ecf20Sopenharmony_ci flags |= MPIC_SINGLE_DEST_CPU; 12678c2ecf20Sopenharmony_ci if (of_device_is_compatible(node, "fsl,mpic")) { 12688c2ecf20Sopenharmony_ci flags |= MPIC_FSL | MPIC_LARGE_VECTORS; 12698c2ecf20Sopenharmony_ci mpic_irq_chip.flags |= IRQCHIP_SKIP_SET_WAKE; 12708c2ecf20Sopenharmony_ci mpic_tm_chip.flags |= IRQCHIP_SKIP_SET_WAKE; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL); 12748c2ecf20Sopenharmony_ci if (mpic == NULL) 12758c2ecf20Sopenharmony_ci goto err_of_node_put; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci mpic->name = name; 12788c2ecf20Sopenharmony_ci mpic->node = node; 12798c2ecf20Sopenharmony_ci mpic->paddr = phys_addr; 12808c2ecf20Sopenharmony_ci mpic->flags = flags; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci mpic->hc_irq = mpic_irq_chip; 12838c2ecf20Sopenharmony_ci mpic->hc_irq.name = name; 12848c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 12858c2ecf20Sopenharmony_ci mpic->hc_irq.irq_set_affinity = mpic_set_affinity; 12868c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 12878c2ecf20Sopenharmony_ci mpic->hc_ht_irq = mpic_irq_ht_chip; 12888c2ecf20Sopenharmony_ci mpic->hc_ht_irq.name = name; 12898c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 12908c2ecf20Sopenharmony_ci mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity; 12918c2ecf20Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 12948c2ecf20Sopenharmony_ci mpic->hc_ipi = mpic_ipi_chip; 12958c2ecf20Sopenharmony_ci mpic->hc_ipi.name = name; 12968c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci mpic->hc_tm = mpic_tm_chip; 12998c2ecf20Sopenharmony_ci mpic->hc_tm.name = name; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci mpic->num_sources = 0; /* so far */ 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_LARGE_VECTORS) 13048c2ecf20Sopenharmony_ci intvec_top = 2047; 13058c2ecf20Sopenharmony_ci else 13068c2ecf20Sopenharmony_ci intvec_top = 255; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci mpic->timer_vecs[0] = intvec_top - 12; 13098c2ecf20Sopenharmony_ci mpic->timer_vecs[1] = intvec_top - 11; 13108c2ecf20Sopenharmony_ci mpic->timer_vecs[2] = intvec_top - 10; 13118c2ecf20Sopenharmony_ci mpic->timer_vecs[3] = intvec_top - 9; 13128c2ecf20Sopenharmony_ci mpic->timer_vecs[4] = intvec_top - 8; 13138c2ecf20Sopenharmony_ci mpic->timer_vecs[5] = intvec_top - 7; 13148c2ecf20Sopenharmony_ci mpic->timer_vecs[6] = intvec_top - 6; 13158c2ecf20Sopenharmony_ci mpic->timer_vecs[7] = intvec_top - 5; 13168c2ecf20Sopenharmony_ci mpic->ipi_vecs[0] = intvec_top - 4; 13178c2ecf20Sopenharmony_ci mpic->ipi_vecs[1] = intvec_top - 3; 13188c2ecf20Sopenharmony_ci mpic->ipi_vecs[2] = intvec_top - 2; 13198c2ecf20Sopenharmony_ci mpic->ipi_vecs[3] = intvec_top - 1; 13208c2ecf20Sopenharmony_ci mpic->spurious_vec = intvec_top; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* Look for protected sources */ 13238c2ecf20Sopenharmony_ci psrc = of_get_property(mpic->node, "protected-sources", &psize); 13248c2ecf20Sopenharmony_ci if (psrc) { 13258c2ecf20Sopenharmony_ci /* Allocate a bitmap with one bit per interrupt */ 13268c2ecf20Sopenharmony_ci unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1); 13278c2ecf20Sopenharmony_ci mpic->protected = kcalloc(mapsize, sizeof(long), GFP_KERNEL); 13288c2ecf20Sopenharmony_ci BUG_ON(mpic->protected == NULL); 13298c2ecf20Sopenharmony_ci for (i = 0; i < psize/sizeof(u32); i++) { 13308c2ecf20Sopenharmony_ci if (psrc[i] > intvec_top) 13318c2ecf20Sopenharmony_ci continue; 13328c2ecf20Sopenharmony_ci __set_bit(psrc[i], mpic->protected); 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_WEIRD 13378c2ecf20Sopenharmony_ci mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)]; 13388c2ecf20Sopenharmony_ci#endif 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* default register type */ 13418c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_BIG_ENDIAN) 13428c2ecf20Sopenharmony_ci mpic->reg_type = mpic_access_mmio_be; 13438c2ecf20Sopenharmony_ci else 13448c2ecf20Sopenharmony_ci mpic->reg_type = mpic_access_mmio_le; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* 13478c2ecf20Sopenharmony_ci * An MPIC with a "dcr-reg" property must be accessed that way, but 13488c2ecf20Sopenharmony_ci * only if the kernel includes DCR support. 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR 13518c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_USES_DCR) 13528c2ecf20Sopenharmony_ci mpic->reg_type = mpic_access_dcr; 13538c2ecf20Sopenharmony_ci#else 13548c2ecf20Sopenharmony_ci BUG_ON(mpic->flags & MPIC_USES_DCR); 13558c2ecf20Sopenharmony_ci#endif 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* Map the global registers */ 13588c2ecf20Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); 13598c2ecf20Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 13628c2ecf20Sopenharmony_ci int ret; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* 13658c2ecf20Sopenharmony_ci * Yes, Freescale really did put global registers in the 13668c2ecf20Sopenharmony_ci * magic per-cpu area -- and they don't even show up in the 13678c2ecf20Sopenharmony_ci * non-magic per-cpu copies that this driver normally uses. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, 13708c2ecf20Sopenharmony_ci MPIC_CPU_THISBASE, 0x1000); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci fsl_version = fsl_mpic_get_version(mpic); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* Error interrupt mask register (EIMR) is required for 13758c2ecf20Sopenharmony_ci * handling individual device error interrupts. EIMR 13768c2ecf20Sopenharmony_ci * was added in MPIC version 4.1. 13778c2ecf20Sopenharmony_ci * 13788c2ecf20Sopenharmony_ci * Over here we reserve vector number space for error 13798c2ecf20Sopenharmony_ci * interrupt vectors. This space is stolen from the 13808c2ecf20Sopenharmony_ci * global vector number space, as in case of ipis 13818c2ecf20Sopenharmony_ci * and timer interrupts. 13828c2ecf20Sopenharmony_ci * 13838c2ecf20Sopenharmony_ci * Available vector space = intvec_top - 13, where 13 13848c2ecf20Sopenharmony_ci * is the number of vectors which have been consumed by 13858c2ecf20Sopenharmony_ci * ipis, timer interrupts and spurious. 13868c2ecf20Sopenharmony_ci */ 13878c2ecf20Sopenharmony_ci if (fsl_version >= 0x401) { 13888c2ecf20Sopenharmony_ci ret = mpic_setup_error_int(mpic, intvec_top - 13); 13898c2ecf20Sopenharmony_ci if (ret) 13908c2ecf20Sopenharmony_ci return NULL; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* 13968c2ecf20Sopenharmony_ci * EPR is only available starting with v4.0. To support 13978c2ecf20Sopenharmony_ci * platforms that don't know the MPIC version at compile-time, 13988c2ecf20Sopenharmony_ci * such as qemu-e500, turn off coreint if this MPIC doesn't 13998c2ecf20Sopenharmony_ci * support it. Note that we never enable it if it wasn't 14008c2ecf20Sopenharmony_ci * requested in the first place. 14018c2ecf20Sopenharmony_ci * 14028c2ecf20Sopenharmony_ci * This is done outside the MPIC_FSL check, so that we 14038c2ecf20Sopenharmony_ci * also disable coreint if the MPIC node doesn't have 14048c2ecf20Sopenharmony_ci * an "fsl,mpic" compatible at all. This will be the case 14058c2ecf20Sopenharmony_ci * with device trees generated by older versions of QEMU. 14068c2ecf20Sopenharmony_ci * fsl_version will be zero if MPIC_FSL is not set. 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) { 14098c2ecf20Sopenharmony_ci WARN_ON(ppc_md.get_irq != mpic_get_coreint_irq); 14108c2ecf20Sopenharmony_ci ppc_md.get_irq = mpic_get_irq; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* Reset */ 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* When using a device-node, reset requests are only honored if the MPIC 14168c2ecf20Sopenharmony_ci * is allowed to reset. 14178c2ecf20Sopenharmony_ci */ 14188c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_NO_RESET)) { 14198c2ecf20Sopenharmony_ci printk(KERN_DEBUG "mpic: Resetting\n"); 14208c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 14218c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 14228c2ecf20Sopenharmony_ci | MPIC_GREG_GCONF_RESET); 14238c2ecf20Sopenharmony_ci while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 14248c2ecf20Sopenharmony_ci & MPIC_GREG_GCONF_RESET) 14258c2ecf20Sopenharmony_ci mb(); 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* CoreInt */ 14298c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_ENABLE_COREINT) 14308c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 14318c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 14328c2ecf20Sopenharmony_ci | MPIC_GREG_GCONF_COREINT); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_ENABLE_MCK) 14358c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 14368c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 14378c2ecf20Sopenharmony_ci | MPIC_GREG_GCONF_MCK); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci /* 14408c2ecf20Sopenharmony_ci * The MPIC driver will crash if there are more cores than we 14418c2ecf20Sopenharmony_ci * can initialize, so we may as well catch that problem here. 14428c2ecf20Sopenharmony_ci */ 14438c2ecf20Sopenharmony_ci BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* Map the per-CPU registers */ 14468c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 14478c2ecf20Sopenharmony_ci unsigned int cpu = get_hard_smp_processor_id(i); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu], 14508c2ecf20Sopenharmony_ci MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 14518c2ecf20Sopenharmony_ci 0x1000); 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* 14558c2ecf20Sopenharmony_ci * Read feature register. For non-ISU MPICs, num sources as well. On 14568c2ecf20Sopenharmony_ci * ISU MPICs, sources are counted as ISUs are added 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_ci greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* 14618c2ecf20Sopenharmony_ci * By default, the last source number comes from the MPIC, but the 14628c2ecf20Sopenharmony_ci * device-tree and board support code can override it on buggy hw. 14638c2ecf20Sopenharmony_ci * If we get passed an isu_size (multi-isu MPIC) then we use that 14648c2ecf20Sopenharmony_ci * as a default instead of the value read from the HW. 14658c2ecf20Sopenharmony_ci */ 14668c2ecf20Sopenharmony_ci last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) 14678c2ecf20Sopenharmony_ci >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; 14688c2ecf20Sopenharmony_ci if (isu_size) 14698c2ecf20Sopenharmony_ci last_irq = isu_size * MPIC_MAX_ISU - 1; 14708c2ecf20Sopenharmony_ci of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); 14718c2ecf20Sopenharmony_ci if (irq_count) 14728c2ecf20Sopenharmony_ci last_irq = irq_count - 1; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* Initialize main ISU if none provided */ 14758c2ecf20Sopenharmony_ci if (!isu_size) { 14768c2ecf20Sopenharmony_ci isu_size = last_irq + 1; 14778c2ecf20Sopenharmony_ci mpic->num_sources = isu_size; 14788c2ecf20Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->isus[0], 14798c2ecf20Sopenharmony_ci MPIC_INFO(IRQ_BASE), 14808c2ecf20Sopenharmony_ci MPIC_INFO(IRQ_STRIDE) * isu_size); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci mpic->isu_size = isu_size; 14848c2ecf20Sopenharmony_ci mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); 14858c2ecf20Sopenharmony_ci mpic->isu_mask = (1 << mpic->isu_shift) - 1; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci mpic->irqhost = irq_domain_add_linear(mpic->node, 14888c2ecf20Sopenharmony_ci intvec_top, 14898c2ecf20Sopenharmony_ci &mpic_host_ops, mpic); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* 14928c2ecf20Sopenharmony_ci * FIXME: The code leaks the MPIC object and mappings here; this 14938c2ecf20Sopenharmony_ci * is very unlikely to fail but it ought to be fixed anyways. 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_ci if (mpic->irqhost == NULL) 14968c2ecf20Sopenharmony_ci return NULL; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci /* Display version */ 14998c2ecf20Sopenharmony_ci switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) { 15008c2ecf20Sopenharmony_ci case 1: 15018c2ecf20Sopenharmony_ci vers = "1.0"; 15028c2ecf20Sopenharmony_ci break; 15038c2ecf20Sopenharmony_ci case 2: 15048c2ecf20Sopenharmony_ci vers = "1.2"; 15058c2ecf20Sopenharmony_ci break; 15068c2ecf20Sopenharmony_ci case 3: 15078c2ecf20Sopenharmony_ci vers = "1.3"; 15088c2ecf20Sopenharmony_ci break; 15098c2ecf20Sopenharmony_ci default: 15108c2ecf20Sopenharmony_ci vers = "<unknown>"; 15118c2ecf20Sopenharmony_ci break; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," 15148c2ecf20Sopenharmony_ci " max %d CPUs\n", 15158c2ecf20Sopenharmony_ci name, vers, (unsigned long long)mpic->paddr, num_possible_cpus()); 15168c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", 15178c2ecf20Sopenharmony_ci mpic->isu_size, mpic->isu_shift, mpic->isu_mask); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci mpic->next = mpics; 15208c2ecf20Sopenharmony_ci mpics = mpic; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) { 15238c2ecf20Sopenharmony_ci mpic_primary = mpic; 15248c2ecf20Sopenharmony_ci irq_set_default_host(mpic->irqhost); 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return mpic; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cierr_of_node_put: 15308c2ecf20Sopenharmony_ci of_node_put(node); 15318c2ecf20Sopenharmony_ci return NULL; 15328c2ecf20Sopenharmony_ci} 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_civoid __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, 15358c2ecf20Sopenharmony_ci phys_addr_t paddr) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci unsigned int isu_first = isu_num * mpic->isu_size; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci BUG_ON(isu_num >= MPIC_MAX_ISU); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci mpic_map(mpic, 15428c2ecf20Sopenharmony_ci paddr, &mpic->isus[isu_num], 0, 15438c2ecf20Sopenharmony_ci MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if ((isu_first + mpic->isu_size) > mpic->num_sources) 15468c2ecf20Sopenharmony_ci mpic->num_sources = isu_first + mpic->isu_size; 15478c2ecf20Sopenharmony_ci} 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_civoid __init mpic_init(struct mpic *mpic) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci int i, cpu; 15528c2ecf20Sopenharmony_ci int num_timers = 4; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci BUG_ON(mpic->num_sources == 0); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* Set current processor priority to max */ 15598c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 15628c2ecf20Sopenharmony_ci u32 version = fsl_mpic_get_version(mpic); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* 15658c2ecf20Sopenharmony_ci * Timer group B is present at the latest in MPIC 3.1 (e.g. 15668c2ecf20Sopenharmony_ci * mpc8536). It is not present in MPIC 2.0 (e.g. mpc8544). 15678c2ecf20Sopenharmony_ci * I don't know about the status of intermediate versions (or 15688c2ecf20Sopenharmony_ci * whether they even exist). 15698c2ecf20Sopenharmony_ci */ 15708c2ecf20Sopenharmony_ci if (version >= 0x0301) 15718c2ecf20Sopenharmony_ci num_timers = 8; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* Initialize timers to our reserved vectors and mask them for now */ 15758c2ecf20Sopenharmony_ci for (i = 0; i < num_timers; i++) { 15768c2ecf20Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, i); 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci mpic_write(mpic->tmregs, 15798c2ecf20Sopenharmony_ci offset + MPIC_INFO(TIMER_DESTINATION), 15808c2ecf20Sopenharmony_ci 1 << hard_smp_processor_id()); 15818c2ecf20Sopenharmony_ci mpic_write(mpic->tmregs, 15828c2ecf20Sopenharmony_ci offset + MPIC_INFO(TIMER_VECTOR_PRI), 15838c2ecf20Sopenharmony_ci MPIC_VECPRI_MASK | 15848c2ecf20Sopenharmony_ci (9 << MPIC_VECPRI_PRIORITY_SHIFT) | 15858c2ecf20Sopenharmony_ci (mpic->timer_vecs[0] + i)); 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* Initialize IPIs to our reserved vectors and mark them disabled for now */ 15898c2ecf20Sopenharmony_ci mpic_test_broken_ipi(mpic); 15908c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 15918c2ecf20Sopenharmony_ci mpic_ipi_write(i, 15928c2ecf20Sopenharmony_ci MPIC_VECPRI_MASK | 15938c2ecf20Sopenharmony_ci (10 << MPIC_VECPRI_PRIORITY_SHIFT) | 15948c2ecf20Sopenharmony_ci (mpic->ipi_vecs[0] + i)); 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci /* Do the HT PIC fixups on U3 broken mpic */ 15988c2ecf20Sopenharmony_ci DBG("MPIC flags: %x\n", mpic->flags); 15998c2ecf20Sopenharmony_ci if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) { 16008c2ecf20Sopenharmony_ci mpic_scan_ht_pics(mpic); 16018c2ecf20Sopenharmony_ci mpic_u3msi_init(mpic); 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci mpic_pasemi_msi_init(mpic); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci cpu = mpic_processor_id(mpic); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_NO_RESET)) { 16098c2ecf20Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 16108c2ecf20Sopenharmony_ci /* start with vector = source number, and masked */ 16118c2ecf20Sopenharmony_ci u32 vecpri = MPIC_VECPRI_MASK | i | 16128c2ecf20Sopenharmony_ci (8 << MPIC_VECPRI_PRIORITY_SHIFT); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci /* check if protected */ 16158c2ecf20Sopenharmony_ci if (mpic->protected && test_bit(i, mpic->protected)) 16168c2ecf20Sopenharmony_ci continue; 16178c2ecf20Sopenharmony_ci /* init hw */ 16188c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 16198c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* Init spurious vector */ 16248c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci /* Disable 8259 passthrough, if supported */ 16278c2ecf20Sopenharmony_ci if (!(mpic->flags & MPIC_NO_PTHROU_DIS)) 16288c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 16298c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 16308c2ecf20Sopenharmony_ci | MPIC_GREG_GCONF_8259_PTHROU_DIS); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_NO_BIAS) 16338c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 16348c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 16358c2ecf20Sopenharmony_ci | MPIC_GREG_GCONF_NO_BIAS); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* Set current processor priority to 0 */ 16388c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16418c2ecf20Sopenharmony_ci /* allocate memory to save mpic state */ 16428c2ecf20Sopenharmony_ci mpic->save_data = kmalloc_array(mpic->num_sources, 16438c2ecf20Sopenharmony_ci sizeof(*mpic->save_data), 16448c2ecf20Sopenharmony_ci GFP_KERNEL); 16458c2ecf20Sopenharmony_ci BUG_ON(mpic->save_data == NULL); 16468c2ecf20Sopenharmony_ci#endif 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci /* Check if this MPIC is chained from a parent interrupt controller */ 16498c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_SECONDARY) { 16508c2ecf20Sopenharmony_ci int virq = irq_of_parse_and_map(mpic->node, 0); 16518c2ecf20Sopenharmony_ci if (virq) { 16528c2ecf20Sopenharmony_ci printk(KERN_INFO "%pOF: hooking up to IRQ %d\n", 16538c2ecf20Sopenharmony_ci mpic->node, virq); 16548c2ecf20Sopenharmony_ci irq_set_handler_data(virq, mpic); 16558c2ecf20Sopenharmony_ci irq_set_chained_handler(virq, &mpic_cascade); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci /* FSL mpic error interrupt initialization */ 16608c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_FSL_HAS_EIMR) 16618c2ecf20Sopenharmony_ci mpic_err_int_init(mpic, MPIC_FSL_ERR_INT); 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_civoid mpic_irq_set_priority(unsigned int irq, unsigned int pri) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_find(irq); 16678c2ecf20Sopenharmony_ci unsigned int src = virq_to_hw(irq); 16688c2ecf20Sopenharmony_ci unsigned long flags; 16698c2ecf20Sopenharmony_ci u32 reg; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci if (!mpic) 16728c2ecf20Sopenharmony_ci return; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 16758c2ecf20Sopenharmony_ci if (mpic_is_ipi(mpic, src)) { 16768c2ecf20Sopenharmony_ci reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & 16778c2ecf20Sopenharmony_ci ~MPIC_VECPRI_PRIORITY_MASK; 16788c2ecf20Sopenharmony_ci mpic_ipi_write(src - mpic->ipi_vecs[0], 16798c2ecf20Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 16808c2ecf20Sopenharmony_ci } else if (mpic_is_tm(mpic, src)) { 16818c2ecf20Sopenharmony_ci reg = mpic_tm_read(src - mpic->timer_vecs[0]) & 16828c2ecf20Sopenharmony_ci ~MPIC_VECPRI_PRIORITY_MASK; 16838c2ecf20Sopenharmony_ci mpic_tm_write(src - mpic->timer_vecs[0], 16848c2ecf20Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 16858c2ecf20Sopenharmony_ci } else { 16868c2ecf20Sopenharmony_ci reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) 16878c2ecf20Sopenharmony_ci & ~MPIC_VECPRI_PRIORITY_MASK; 16888c2ecf20Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 16898c2ecf20Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_civoid mpic_setup_this_cpu(void) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 16978c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 16988c2ecf20Sopenharmony_ci unsigned long flags; 16998c2ecf20Sopenharmony_ci u32 msk = 1 << hard_smp_processor_id(); 17008c2ecf20Sopenharmony_ci unsigned int i; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci /* let the mpic know we want intrs. default affinity is 0xffffffff 17098c2ecf20Sopenharmony_ci * until changed via /proc. That's how it's done on x86. If we want 17108c2ecf20Sopenharmony_ci * it differently, then we should make sure we also change the default 17118c2ecf20Sopenharmony_ci * values of irq_desc[].affinity in irq.c. 17128c2ecf20Sopenharmony_ci */ 17138c2ecf20Sopenharmony_ci if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) { 17148c2ecf20Sopenharmony_ci for (i = 0; i < mpic->num_sources ; i++) 17158c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 17168c2ecf20Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk); 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci /* Set current processor priority to 0 */ 17208c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 17238c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ciint mpic_cpu_get_priority(void) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI)); 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_civoid mpic_cpu_set_priority(int prio) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci prio &= MPIC_CPU_TASKPRI_MASK; 17388c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio); 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_civoid mpic_teardown_this_cpu(int secondary) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 17448c2ecf20Sopenharmony_ci unsigned long flags; 17458c2ecf20Sopenharmony_ci u32 msk = 1 << hard_smp_processor_id(); 17468c2ecf20Sopenharmony_ci unsigned int i; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 17518c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* let the mpic know we don't want intrs. */ 17548c2ecf20Sopenharmony_ci for (i = 0; i < mpic->num_sources ; i++) 17558c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 17568c2ecf20Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci /* Set current processor priority to max */ 17598c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 17608c2ecf20Sopenharmony_ci /* We need to EOI the IPI since not all platforms reset the MPIC 17618c2ecf20Sopenharmony_ci * on boot and new interrupts wouldn't get delivered otherwise. 17628c2ecf20Sopenharmony_ci */ 17638c2ecf20Sopenharmony_ci mpic_eoi(mpic); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_cistatic unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg) 17708c2ecf20Sopenharmony_ci{ 17718c2ecf20Sopenharmony_ci u32 src; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci src = mpic_cpu_read(reg) & MPIC_INFO(VECPRI_VECTOR_MASK); 17748c2ecf20Sopenharmony_ci#ifdef DEBUG_LOW 17758c2ecf20Sopenharmony_ci DBG("%s: get_one_irq(reg 0x%x): %d\n", mpic->name, reg, src); 17768c2ecf20Sopenharmony_ci#endif 17778c2ecf20Sopenharmony_ci if (unlikely(src == mpic->spurious_vec)) { 17788c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_SPV_EOI) 17798c2ecf20Sopenharmony_ci mpic_eoi(mpic); 17808c2ecf20Sopenharmony_ci return 0; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 17838c2ecf20Sopenharmony_ci printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 17848c2ecf20Sopenharmony_ci mpic->name, (int)src); 17858c2ecf20Sopenharmony_ci mpic_eoi(mpic); 17868c2ecf20Sopenharmony_ci return 0; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci return irq_linear_revmap(mpic->irqhost, src); 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ciunsigned int mpic_get_one_irq(struct mpic *mpic) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_INTACK)); 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ciunsigned int mpic_get_irq(void) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci return mpic_get_one_irq(mpic); 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ciunsigned int mpic_get_coreint_irq(void) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOKE 18098c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 18108c2ecf20Sopenharmony_ci u32 src; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci src = mfspr(SPRN_EPR); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci if (unlikely(src == mpic->spurious_vec)) { 18178c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_SPV_EOI) 18188c2ecf20Sopenharmony_ci mpic_eoi(mpic); 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 18228c2ecf20Sopenharmony_ci printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 18238c2ecf20Sopenharmony_ci mpic->name, (int)src); 18248c2ecf20Sopenharmony_ci return 0; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci return irq_linear_revmap(mpic->irqhost, src); 18288c2ecf20Sopenharmony_ci#else 18298c2ecf20Sopenharmony_ci return 0; 18308c2ecf20Sopenharmony_ci#endif 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ciunsigned int mpic_get_mcirq(void) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_MCACK)); 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 18438c2ecf20Sopenharmony_civoid mpic_request_ipis(void) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 18468c2ecf20Sopenharmony_ci int i; 18478c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci printk(KERN_INFO "mpic: requesting IPIs...\n"); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 18528c2ecf20Sopenharmony_ci unsigned int vipi = irq_create_mapping(mpic->irqhost, 18538c2ecf20Sopenharmony_ci mpic->ipi_vecs[0] + i); 18548c2ecf20Sopenharmony_ci if (!vipi) { 18558c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]); 18568c2ecf20Sopenharmony_ci continue; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci smp_request_message_ipi(vipi, i); 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_civoid smp_mpic_message_pass(int cpu, int msg) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 18658c2ecf20Sopenharmony_ci u32 physmask; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci BUG_ON(mpic == NULL); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* make sure we're sending something that translates to an IPI */ 18708c2ecf20Sopenharmony_ci if ((unsigned int)msg > 3) { 18718c2ecf20Sopenharmony_ci printk("SMP %d: smp_message_pass: unknown msg %d\n", 18728c2ecf20Sopenharmony_ci smp_processor_id(), msg); 18738c2ecf20Sopenharmony_ci return; 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci#ifdef DEBUG_IPI 18778c2ecf20Sopenharmony_ci DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg); 18788c2ecf20Sopenharmony_ci#endif 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci physmask = 1 << get_hard_smp_processor_id(cpu); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + 18838c2ecf20Sopenharmony_ci msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask); 18848c2ecf20Sopenharmony_ci} 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_civoid __init smp_mpic_probe(void) 18878c2ecf20Sopenharmony_ci{ 18888c2ecf20Sopenharmony_ci int nr_cpus; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci DBG("smp_mpic_probe()...\n"); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci nr_cpus = num_possible_cpus(); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci DBG("nr_cpus: %d\n", nr_cpus); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (nr_cpus > 1) 18978c2ecf20Sopenharmony_ci mpic_request_ipis(); 18988c2ecf20Sopenharmony_ci} 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_civoid smp_mpic_setup_cpu(int cpu) 19018c2ecf20Sopenharmony_ci{ 19028c2ecf20Sopenharmony_ci mpic_setup_this_cpu(); 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_civoid mpic_reset_core(int cpu) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci struct mpic *mpic = mpic_primary; 19088c2ecf20Sopenharmony_ci u32 pir; 19098c2ecf20Sopenharmony_ci int cpuid = get_hard_smp_processor_id(cpu); 19108c2ecf20Sopenharmony_ci int i; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci /* Set target bit for core reset */ 19138c2ecf20Sopenharmony_ci pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 19148c2ecf20Sopenharmony_ci pir |= (1 << cpuid); 19158c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 19168c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci /* Restore target bit after reset complete */ 19198c2ecf20Sopenharmony_ci pir &= ~(1 << cpuid); 19208c2ecf20Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 19218c2ecf20Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci /* Perform 15 EOI on each reset core to clear pending interrupts. 19248c2ecf20Sopenharmony_ci * This is required for FSL CoreNet based devices */ 19258c2ecf20Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 19268c2ecf20Sopenharmony_ci for (i = 0; i < 15; i++) { 19278c2ecf20Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid], 19288c2ecf20Sopenharmony_ci MPIC_CPU_EOI, 0); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 19358c2ecf20Sopenharmony_cistatic void mpic_suspend_one(struct mpic *mpic) 19368c2ecf20Sopenharmony_ci{ 19378c2ecf20Sopenharmony_ci int i; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 19408c2ecf20Sopenharmony_ci mpic->save_data[i].vecprio = 19418c2ecf20Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); 19428c2ecf20Sopenharmony_ci mpic->save_data[i].dest = 19438c2ecf20Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci} 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_cistatic int mpic_suspend(void) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci struct mpic *mpic = mpics; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci while (mpic) { 19528c2ecf20Sopenharmony_ci mpic_suspend_one(mpic); 19538c2ecf20Sopenharmony_ci mpic = mpic->next; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci return 0; 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_cistatic void mpic_resume_one(struct mpic *mpic) 19608c2ecf20Sopenharmony_ci{ 19618c2ecf20Sopenharmony_ci int i; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 19648c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), 19658c2ecf20Sopenharmony_ci mpic->save_data[i].vecprio); 19668c2ecf20Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 19678c2ecf20Sopenharmony_ci mpic->save_data[i].dest); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 19708c2ecf20Sopenharmony_ci if (mpic->fixups) { 19718c2ecf20Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[i]; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci if (fixup->base) { 19748c2ecf20Sopenharmony_ci /* we use the lowest bit in an inverted meaning */ 19758c2ecf20Sopenharmony_ci if ((mpic->save_data[i].fixup_data & 1) == 0) 19768c2ecf20Sopenharmony_ci continue; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* Enable and configure */ 19798c2ecf20Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci writel(mpic->save_data[i].fixup_data & ~1, 19828c2ecf20Sopenharmony_ci fixup->base + 4); 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci#endif 19868c2ecf20Sopenharmony_ci } /* end for loop */ 19878c2ecf20Sopenharmony_ci} 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_cistatic void mpic_resume(void) 19908c2ecf20Sopenharmony_ci{ 19918c2ecf20Sopenharmony_ci struct mpic *mpic = mpics; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci while (mpic) { 19948c2ecf20Sopenharmony_ci mpic_resume_one(mpic); 19958c2ecf20Sopenharmony_ci mpic = mpic->next; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_cistatic struct syscore_ops mpic_syscore_ops = { 20008c2ecf20Sopenharmony_ci .resume = mpic_resume, 20018c2ecf20Sopenharmony_ci .suspend = mpic_suspend, 20028c2ecf20Sopenharmony_ci}; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_cistatic int mpic_init_sys(void) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci int rc; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci register_syscore_ops(&mpic_syscore_ops); 20098c2ecf20Sopenharmony_ci rc = subsys_system_register(&mpic_subsys, NULL); 20108c2ecf20Sopenharmony_ci if (rc) { 20118c2ecf20Sopenharmony_ci unregister_syscore_ops(&mpic_syscore_ops); 20128c2ecf20Sopenharmony_ci pr_err("mpic: Failed to register subsystem!\n"); 20138c2ecf20Sopenharmony_ci return rc; 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci return 0; 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cidevice_initcall(mpic_init_sys); 20208c2ecf20Sopenharmony_ci#endif 2021