162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * arch/powerpc/kernel/mpic.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Driver for interrupt controllers following the OpenPIC standard, the 562306a36Sopenharmony_ci * common implementation being IBM's MPIC. This driver also can deal 662306a36Sopenharmony_ci * with various broken implementations of this HW. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. 962306a36Sopenharmony_ci * Copyright 2010-2012 Freescale Semiconductor, Inc. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1262306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 1362306a36Sopenharmony_ci * for more details. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#undef DEBUG 1762306a36Sopenharmony_ci#undef DEBUG_IPI 1862306a36Sopenharmony_ci#undef DEBUG_IRQ 1962306a36Sopenharmony_ci#undef DEBUG_LOW 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/irq.h> 2562306a36Sopenharmony_ci#include <linux/smp.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/pci.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/syscore_ops.h> 3162306a36Sopenharmony_ci#include <linux/ratelimit.h> 3262306a36Sopenharmony_ci#include <linux/pgtable.h> 3362306a36Sopenharmony_ci#include <linux/of_address.h> 3462306a36Sopenharmony_ci#include <linux/of_irq.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <asm/ptrace.h> 3762306a36Sopenharmony_ci#include <asm/signal.h> 3862306a36Sopenharmony_ci#include <asm/io.h> 3962306a36Sopenharmony_ci#include <asm/irq.h> 4062306a36Sopenharmony_ci#include <asm/machdep.h> 4162306a36Sopenharmony_ci#include <asm/mpic.h> 4262306a36Sopenharmony_ci#include <asm/smp.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include "mpic.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#ifdef DEBUG 4762306a36Sopenharmony_ci#define DBG(fmt...) printk(fmt) 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci#define DBG(fmt...) 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct bus_type mpic_subsys = { 5362306a36Sopenharmony_ci .name = "mpic", 5462306a36Sopenharmony_ci .dev_name = "mpic", 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_subsys); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct mpic *mpics; 5962306a36Sopenharmony_cistatic struct mpic *mpic_primary; 6062306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(mpic_lock); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#ifdef CONFIG_PPC32 /* XXX for now */ 6362306a36Sopenharmony_ci#ifdef CONFIG_IRQ_ALL_CPUS 6462306a36Sopenharmony_ci#define distribute_irqs (1) 6562306a36Sopenharmony_ci#else 6662306a36Sopenharmony_ci#define distribute_irqs (0) 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#ifdef CONFIG_MPIC_WEIRD 7162306a36Sopenharmony_cistatic u32 mpic_infos[][MPIC_IDX_END] = { 7262306a36Sopenharmony_ci [0] = { /* Original OpenPIC compatible MPIC */ 7362306a36Sopenharmony_ci MPIC_GREG_BASE, 7462306a36Sopenharmony_ci MPIC_GREG_FEATURE_0, 7562306a36Sopenharmony_ci MPIC_GREG_GLOBAL_CONF_0, 7662306a36Sopenharmony_ci MPIC_GREG_VENDOR_ID, 7762306a36Sopenharmony_ci MPIC_GREG_IPI_VECTOR_PRI_0, 7862306a36Sopenharmony_ci MPIC_GREG_IPI_STRIDE, 7962306a36Sopenharmony_ci MPIC_GREG_SPURIOUS, 8062306a36Sopenharmony_ci MPIC_GREG_TIMER_FREQ, 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci MPIC_TIMER_BASE, 8362306a36Sopenharmony_ci MPIC_TIMER_STRIDE, 8462306a36Sopenharmony_ci MPIC_TIMER_CURRENT_CNT, 8562306a36Sopenharmony_ci MPIC_TIMER_BASE_CNT, 8662306a36Sopenharmony_ci MPIC_TIMER_VECTOR_PRI, 8762306a36Sopenharmony_ci MPIC_TIMER_DESTINATION, 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci MPIC_CPU_BASE, 9062306a36Sopenharmony_ci MPIC_CPU_STRIDE, 9162306a36Sopenharmony_ci MPIC_CPU_IPI_DISPATCH_0, 9262306a36Sopenharmony_ci MPIC_CPU_IPI_DISPATCH_STRIDE, 9362306a36Sopenharmony_ci MPIC_CPU_CURRENT_TASK_PRI, 9462306a36Sopenharmony_ci MPIC_CPU_WHOAMI, 9562306a36Sopenharmony_ci MPIC_CPU_INTACK, 9662306a36Sopenharmony_ci MPIC_CPU_EOI, 9762306a36Sopenharmony_ci MPIC_CPU_MCACK, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci MPIC_IRQ_BASE, 10062306a36Sopenharmony_ci MPIC_IRQ_STRIDE, 10162306a36Sopenharmony_ci MPIC_IRQ_VECTOR_PRI, 10262306a36Sopenharmony_ci MPIC_VECPRI_VECTOR_MASK, 10362306a36Sopenharmony_ci MPIC_VECPRI_POLARITY_POSITIVE, 10462306a36Sopenharmony_ci MPIC_VECPRI_POLARITY_NEGATIVE, 10562306a36Sopenharmony_ci MPIC_VECPRI_SENSE_LEVEL, 10662306a36Sopenharmony_ci MPIC_VECPRI_SENSE_EDGE, 10762306a36Sopenharmony_ci MPIC_VECPRI_POLARITY_MASK, 10862306a36Sopenharmony_ci MPIC_VECPRI_SENSE_MASK, 10962306a36Sopenharmony_ci MPIC_IRQ_DESTINATION 11062306a36Sopenharmony_ci }, 11162306a36Sopenharmony_ci [1] = { /* Tsi108/109 PIC */ 11262306a36Sopenharmony_ci TSI108_GREG_BASE, 11362306a36Sopenharmony_ci TSI108_GREG_FEATURE_0, 11462306a36Sopenharmony_ci TSI108_GREG_GLOBAL_CONF_0, 11562306a36Sopenharmony_ci TSI108_GREG_VENDOR_ID, 11662306a36Sopenharmony_ci TSI108_GREG_IPI_VECTOR_PRI_0, 11762306a36Sopenharmony_ci TSI108_GREG_IPI_STRIDE, 11862306a36Sopenharmony_ci TSI108_GREG_SPURIOUS, 11962306a36Sopenharmony_ci TSI108_GREG_TIMER_FREQ, 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci TSI108_TIMER_BASE, 12262306a36Sopenharmony_ci TSI108_TIMER_STRIDE, 12362306a36Sopenharmony_ci TSI108_TIMER_CURRENT_CNT, 12462306a36Sopenharmony_ci TSI108_TIMER_BASE_CNT, 12562306a36Sopenharmony_ci TSI108_TIMER_VECTOR_PRI, 12662306a36Sopenharmony_ci TSI108_TIMER_DESTINATION, 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci TSI108_CPU_BASE, 12962306a36Sopenharmony_ci TSI108_CPU_STRIDE, 13062306a36Sopenharmony_ci TSI108_CPU_IPI_DISPATCH_0, 13162306a36Sopenharmony_ci TSI108_CPU_IPI_DISPATCH_STRIDE, 13262306a36Sopenharmony_ci TSI108_CPU_CURRENT_TASK_PRI, 13362306a36Sopenharmony_ci TSI108_CPU_WHOAMI, 13462306a36Sopenharmony_ci TSI108_CPU_INTACK, 13562306a36Sopenharmony_ci TSI108_CPU_EOI, 13662306a36Sopenharmony_ci TSI108_CPU_MCACK, 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci TSI108_IRQ_BASE, 13962306a36Sopenharmony_ci TSI108_IRQ_STRIDE, 14062306a36Sopenharmony_ci TSI108_IRQ_VECTOR_PRI, 14162306a36Sopenharmony_ci TSI108_VECPRI_VECTOR_MASK, 14262306a36Sopenharmony_ci TSI108_VECPRI_POLARITY_POSITIVE, 14362306a36Sopenharmony_ci TSI108_VECPRI_POLARITY_NEGATIVE, 14462306a36Sopenharmony_ci TSI108_VECPRI_SENSE_LEVEL, 14562306a36Sopenharmony_ci TSI108_VECPRI_SENSE_EDGE, 14662306a36Sopenharmony_ci TSI108_VECPRI_POLARITY_MASK, 14762306a36Sopenharmony_ci TSI108_VECPRI_SENSE_MASK, 14862306a36Sopenharmony_ci TSI108_IRQ_DESTINATION 14962306a36Sopenharmony_ci }, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name] 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#else /* CONFIG_MPIC_WEIRD */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define MPIC_INFO(name) MPIC_##name 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#endif /* CONFIG_MPIC_WEIRD */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline unsigned int mpic_processor_id(struct mpic *mpic) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned int cpu = 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 16562306a36Sopenharmony_ci cpu = hard_smp_processor_id(); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return cpu; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Register accessor functions 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic inline u32 _mpic_read(enum mpic_reg_type type, 17662306a36Sopenharmony_ci struct mpic_reg_bank *rb, 17762306a36Sopenharmony_ci unsigned int reg) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci switch(type) { 18062306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR 18162306a36Sopenharmony_ci case mpic_access_dcr: 18262306a36Sopenharmony_ci return dcr_read(rb->dhost, reg); 18362306a36Sopenharmony_ci#endif 18462306a36Sopenharmony_ci case mpic_access_mmio_be: 18562306a36Sopenharmony_ci return in_be32(rb->base + (reg >> 2)); 18662306a36Sopenharmony_ci case mpic_access_mmio_le: 18762306a36Sopenharmony_ci default: 18862306a36Sopenharmony_ci return in_le32(rb->base + (reg >> 2)); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline void _mpic_write(enum mpic_reg_type type, 19362306a36Sopenharmony_ci struct mpic_reg_bank *rb, 19462306a36Sopenharmony_ci unsigned int reg, u32 value) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci switch(type) { 19762306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR 19862306a36Sopenharmony_ci case mpic_access_dcr: 19962306a36Sopenharmony_ci dcr_write(rb->dhost, reg, value); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci#endif 20262306a36Sopenharmony_ci case mpic_access_mmio_be: 20362306a36Sopenharmony_ci out_be32(rb->base + (reg >> 2), value); 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case mpic_access_mmio_le: 20662306a36Sopenharmony_ci default: 20762306a36Sopenharmony_ci out_le32(rb->base + (reg >> 2), value); 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci enum mpic_reg_type type = mpic->reg_type; 21562306a36Sopenharmony_ci unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 21662306a36Sopenharmony_ci (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le) 21962306a36Sopenharmony_ci type = mpic_access_mmio_be; 22062306a36Sopenharmony_ci return _mpic_read(type, &mpic->gregs, offset); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 22662306a36Sopenharmony_ci (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE + 23462306a36Sopenharmony_ci (tm & 3) * MPIC_INFO(TIMER_STRIDE); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, tm) + 24062306a36Sopenharmony_ci MPIC_INFO(TIMER_VECTOR_PRI); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return _mpic_read(mpic->reg_type, &mpic->tmregs, offset); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, tm) + 24862306a36Sopenharmony_ci MPIC_INFO(TIMER_VECTOR_PRI); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci unsigned int cpu = mpic_processor_id(mpic); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci unsigned int cpu = mpic_processor_id(mpic); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci unsigned int isu = src_no >> mpic->isu_shift; 27062306a36Sopenharmony_ci unsigned int idx = src_no & mpic->isu_mask; 27162306a36Sopenharmony_ci unsigned int val; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci val = _mpic_read(mpic->reg_type, &mpic->isus[isu], 27462306a36Sopenharmony_ci reg + (idx * MPIC_INFO(IRQ_STRIDE))); 27562306a36Sopenharmony_ci#ifdef CONFIG_MPIC_BROKEN_REGREAD 27662306a36Sopenharmony_ci if (reg == 0) 27762306a36Sopenharmony_ci val = (val & (MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY)) | 27862306a36Sopenharmony_ci mpic->isu_reg0_shadow[src_no]; 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci return val; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, 28462306a36Sopenharmony_ci unsigned int reg, u32 value) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci unsigned int isu = src_no >> mpic->isu_shift; 28762306a36Sopenharmony_ci unsigned int idx = src_no & mpic->isu_mask; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->isus[isu], 29062306a36Sopenharmony_ci reg + (idx * MPIC_INFO(IRQ_STRIDE)), value); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#ifdef CONFIG_MPIC_BROKEN_REGREAD 29362306a36Sopenharmony_ci if (reg == 0) 29462306a36Sopenharmony_ci mpic->isu_reg0_shadow[src_no] = 29562306a36Sopenharmony_ci value & ~(MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY); 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#define mpic_read(b,r) _mpic_read(mpic->reg_type,&(b),(r)) 30062306a36Sopenharmony_ci#define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) 30162306a36Sopenharmony_ci#define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) 30262306a36Sopenharmony_ci#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) 30362306a36Sopenharmony_ci#define mpic_tm_read(i) _mpic_tm_read(mpic,(i)) 30462306a36Sopenharmony_ci#define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v)) 30562306a36Sopenharmony_ci#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) 30662306a36Sopenharmony_ci#define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) 30762306a36Sopenharmony_ci#define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) 30862306a36Sopenharmony_ci#define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v)) 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* 31262306a36Sopenharmony_ci * Low level utility functions 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr, 31762306a36Sopenharmony_ci struct mpic_reg_bank *rb, unsigned int offset, 31862306a36Sopenharmony_ci unsigned int size) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci rb->base = ioremap(phys_addr + offset, size); 32162306a36Sopenharmony_ci BUG_ON(rb->base == NULL); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR 32562306a36Sopenharmony_cistatic void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, 32662306a36Sopenharmony_ci unsigned int offset, unsigned int size) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0); 32962306a36Sopenharmony_ci rb->dhost = dcr_map(mpic->node, phys_addr + offset, size); 33062306a36Sopenharmony_ci BUG_ON(!DCR_MAP_OK(rb->dhost)); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic inline void mpic_map(struct mpic *mpic, 33462306a36Sopenharmony_ci phys_addr_t phys_addr, struct mpic_reg_bank *rb, 33562306a36Sopenharmony_ci unsigned int offset, unsigned int size) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci if (mpic->flags & MPIC_USES_DCR) 33862306a36Sopenharmony_ci _mpic_map_dcr(mpic, rb, offset, size); 33962306a36Sopenharmony_ci else 34062306a36Sopenharmony_ci _mpic_map_mmio(mpic, phys_addr, rb, offset, size); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci#else /* CONFIG_PPC_DCR */ 34362306a36Sopenharmony_ci#define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) 34462306a36Sopenharmony_ci#endif /* !CONFIG_PPC_DCR */ 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* Check if we have one of those nice broken MPICs with a flipped endian on 34962306a36Sopenharmony_ci * reads from IPI registers 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic void __init mpic_test_broken_ipi(struct mpic *mpic) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u32 r; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK); 35662306a36Sopenharmony_ci r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0)); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (r == le32_to_cpu(MPIC_VECPRI_MASK)) { 35962306a36Sopenharmony_ci printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); 36062306a36Sopenharmony_ci mpic->flags |= MPIC_BROKEN_IPI; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* Test if an interrupt is sourced from HyperTransport (used on broken U3s) 36762306a36Sopenharmony_ci * to force the edge setting on the MPIC and do the ack workaround. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_cistatic inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci if (source >= 128 || !mpic->fixups) 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci return mpic->fixups[source].base != NULL; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (fixup->applebase) { 38262306a36Sopenharmony_ci unsigned int soff = (fixup->index >> 3) & ~3; 38362306a36Sopenharmony_ci unsigned int mask = 1U << (fixup->index & 0x1f); 38462306a36Sopenharmony_ci writel(mask, fixup->applebase + soff); 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci raw_spin_lock(&mpic->fixup_lock); 38762306a36Sopenharmony_ci writeb(0x11 + 2 * fixup->index, fixup->base + 2); 38862306a36Sopenharmony_ci writel(fixup->data, fixup->base + 4); 38962306a36Sopenharmony_ci raw_spin_unlock(&mpic->fixup_lock); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, 39462306a36Sopenharmony_ci bool level) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 39762306a36Sopenharmony_ci unsigned long flags; 39862306a36Sopenharmony_ci u32 tmp; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (fixup->base == NULL) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci DBG("startup_ht_interrupt(0x%x) index: %d\n", 40462306a36Sopenharmony_ci source, fixup->index); 40562306a36Sopenharmony_ci raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 40662306a36Sopenharmony_ci /* Enable and configure */ 40762306a36Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 40862306a36Sopenharmony_ci tmp = readl(fixup->base + 4); 40962306a36Sopenharmony_ci tmp &= ~(0x23U); 41062306a36Sopenharmony_ci if (level) 41162306a36Sopenharmony_ci tmp |= 0x22; 41262306a36Sopenharmony_ci writel(tmp, fixup->base + 4); 41362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci#ifdef CONFIG_PM 41662306a36Sopenharmony_ci /* use the lowest bit inverted to the actual HW, 41762306a36Sopenharmony_ci * set if this fixup was enabled, clear otherwise */ 41862306a36Sopenharmony_ci mpic->save_data[source].fixup_data = tmp | 1; 41962306a36Sopenharmony_ci#endif 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 42562306a36Sopenharmony_ci unsigned long flags; 42662306a36Sopenharmony_ci u32 tmp; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (fixup->base == NULL) 42962306a36Sopenharmony_ci return; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci DBG("shutdown_ht_interrupt(0x%x)\n", source); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Disable */ 43462306a36Sopenharmony_ci raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 43562306a36Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 43662306a36Sopenharmony_ci tmp = readl(fixup->base + 4); 43762306a36Sopenharmony_ci tmp |= 1; 43862306a36Sopenharmony_ci writel(tmp, fixup->base + 4); 43962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci#ifdef CONFIG_PM 44262306a36Sopenharmony_ci /* use the lowest bit inverted to the actual HW, 44362306a36Sopenharmony_ci * set if this fixup was enabled, clear otherwise */ 44462306a36Sopenharmony_ci mpic->save_data[source].fixup_data = tmp & ~1; 44562306a36Sopenharmony_ci#endif 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 44962306a36Sopenharmony_cistatic void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 45062306a36Sopenharmony_ci unsigned int devfn) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci u8 __iomem *base; 45362306a36Sopenharmony_ci u8 pos, flags; 45462306a36Sopenharmony_ci u64 addr = 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 45762306a36Sopenharmony_ci pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 45862306a36Sopenharmony_ci u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 45962306a36Sopenharmony_ci if (id == PCI_CAP_ID_HT) { 46062306a36Sopenharmony_ci id = readb(devbase + pos + 3); 46162306a36Sopenharmony_ci if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING) 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (pos == 0) 46762306a36Sopenharmony_ci return; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci base = devbase + pos; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci flags = readb(base + HT_MSI_FLAGS); 47262306a36Sopenharmony_ci if (!(flags & HT_MSI_FLAGS_FIXED)) { 47362306a36Sopenharmony_ci addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK; 47462306a36Sopenharmony_ci addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%llx\n", 47862306a36Sopenharmony_ci PCI_SLOT(devfn), PCI_FUNC(devfn), 47962306a36Sopenharmony_ci flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!(flags & HT_MSI_FLAGS_ENABLE)) 48262306a36Sopenharmony_ci writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci#else 48562306a36Sopenharmony_cistatic void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 48662306a36Sopenharmony_ci unsigned int devfn) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci#endif 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, 49362306a36Sopenharmony_ci unsigned int devfn, u32 vdid) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci int i, irq, n; 49662306a36Sopenharmony_ci u8 __iomem *base; 49762306a36Sopenharmony_ci u32 tmp; 49862306a36Sopenharmony_ci u8 pos; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 50162306a36Sopenharmony_ci pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 50262306a36Sopenharmony_ci u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 50362306a36Sopenharmony_ci if (id == PCI_CAP_ID_HT) { 50462306a36Sopenharmony_ci id = readb(devbase + pos + 3); 50562306a36Sopenharmony_ci if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_IRQ) 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci if (pos == 0) 51062306a36Sopenharmony_ci return; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci base = devbase + pos; 51362306a36Sopenharmony_ci writeb(0x01, base + 2); 51462306a36Sopenharmony_ci n = (readl(base + 4) >> 16) & 0xff; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" 51762306a36Sopenharmony_ci " has %d irqs\n", 51862306a36Sopenharmony_ci devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci for (i = 0; i <= n; i++) { 52162306a36Sopenharmony_ci writeb(0x10 + 2 * i, base + 2); 52262306a36Sopenharmony_ci tmp = readl(base + 4); 52362306a36Sopenharmony_ci irq = (tmp >> 16) & 0xff; 52462306a36Sopenharmony_ci DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); 52562306a36Sopenharmony_ci /* mask it , will be unmasked later */ 52662306a36Sopenharmony_ci tmp |= 0x1; 52762306a36Sopenharmony_ci writel(tmp, base + 4); 52862306a36Sopenharmony_ci mpic->fixups[irq].index = i; 52962306a36Sopenharmony_ci mpic->fixups[irq].base = base; 53062306a36Sopenharmony_ci /* Apple HT PIC has a non-standard way of doing EOIs */ 53162306a36Sopenharmony_ci if ((vdid & 0xffff) == 0x106b) 53262306a36Sopenharmony_ci mpic->fixups[irq].applebase = devbase + 0x60; 53362306a36Sopenharmony_ci else 53462306a36Sopenharmony_ci mpic->fixups[irq].applebase = NULL; 53562306a36Sopenharmony_ci writeb(0x11 + 2 * i, base + 2); 53662306a36Sopenharmony_ci mpic->fixups[irq].data = readl(base + 4) | 0x80000000; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void __init mpic_scan_ht_pics(struct mpic *mpic) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci unsigned int devfn; 54462306a36Sopenharmony_ci u8 __iomem *cfgspace; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Allocate fixups array */ 54962306a36Sopenharmony_ci mpic->fixups = kcalloc(128, sizeof(*mpic->fixups), GFP_KERNEL); 55062306a36Sopenharmony_ci BUG_ON(mpic->fixups == NULL); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Init spinlock */ 55362306a36Sopenharmony_ci raw_spin_lock_init(&mpic->fixup_lock); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Map U3 config space. We assume all IO-APICs are on the primary bus 55662306a36Sopenharmony_ci * so we only need to map 64kB. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci cfgspace = ioremap(0xf2000000, 0x10000); 55962306a36Sopenharmony_ci BUG_ON(cfgspace == NULL); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Now we scan all slots. We do a very quick scan, we read the header 56262306a36Sopenharmony_ci * type, vendor ID and device ID only, that's plenty enough 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci for (devfn = 0; devfn < 0x100; devfn++) { 56562306a36Sopenharmony_ci u8 __iomem *devbase = cfgspace + (devfn << 8); 56662306a36Sopenharmony_ci u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); 56762306a36Sopenharmony_ci u32 l = readl(devbase + PCI_VENDOR_ID); 56862306a36Sopenharmony_ci u16 s; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci DBG("devfn %x, l: %x\n", devfn, l); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* If no device, skip */ 57362306a36Sopenharmony_ci if (l == 0xffffffff || l == 0x00000000 || 57462306a36Sopenharmony_ci l == 0x0000ffff || l == 0xffff0000) 57562306a36Sopenharmony_ci goto next; 57662306a36Sopenharmony_ci /* Check if is supports capability lists */ 57762306a36Sopenharmony_ci s = readw(devbase + PCI_STATUS); 57862306a36Sopenharmony_ci if (!(s & PCI_STATUS_CAP_LIST)) 57962306a36Sopenharmony_ci goto next; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mpic_scan_ht_pic(mpic, devbase, devfn, l); 58262306a36Sopenharmony_ci mpic_scan_ht_msi(mpic, devbase, devfn); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci next: 58562306a36Sopenharmony_ci /* next device, if function 0 */ 58662306a36Sopenharmony_ci if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) 58762306a36Sopenharmony_ci devfn += 7; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci#else /* CONFIG_MPIC_U3_HT_IRQS */ 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void __init mpic_scan_ht_pics(struct mpic *mpic) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* Find an mpic associated with a given linux interrupt */ 60562306a36Sopenharmony_cistatic struct mpic *mpic_find(unsigned int irq) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci if (irq < NR_IRQS_LEGACY) 60862306a36Sopenharmony_ci return NULL; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return irq_get_chip_data(irq); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* Determine if the linux irq is an IPI */ 61462306a36Sopenharmony_cistatic unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/* Determine if the linux irq is a timer */ 62062306a36Sopenharmony_cistatic unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/* Convert a cpu mask from logical to physical cpu numbers. */ 62662306a36Sopenharmony_cistatic inline u32 mpic_physmask(u32 cpumask) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci int i; 62962306a36Sopenharmony_ci u32 mask = 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = 0; i < min(32, NR_CPUS) && cpu_possible(i); ++i, cpumask >>= 1) 63262306a36Sopenharmony_ci mask |= (cpumask & 1) << get_hard_smp_processor_id(i); 63362306a36Sopenharmony_ci return mask; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci#ifdef CONFIG_SMP 63762306a36Sopenharmony_ci/* Get the mpic structure from the IPI number */ 63862306a36Sopenharmony_cistatic inline struct mpic * mpic_from_ipi(struct irq_data *d) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci return irq_data_get_irq_chip_data(d); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci#endif 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/* Get the mpic structure from the irq number */ 64562306a36Sopenharmony_cistatic inline struct mpic * mpic_from_irq(unsigned int irq) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci return irq_get_chip_data(irq); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* Get the mpic structure from the irq data */ 65162306a36Sopenharmony_cistatic inline struct mpic * mpic_from_irq_data(struct irq_data *d) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci return irq_data_get_irq_chip_data(d); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* Send an EOI */ 65762306a36Sopenharmony_cistatic inline void mpic_eoi(struct mpic *mpic) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_EOI), 0); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci/* 66362306a36Sopenharmony_ci * Linux descriptor level callbacks 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_civoid mpic_unmask_irq(struct irq_data *d) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci unsigned int loops = 100000; 67062306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 67162306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 67662306a36Sopenharmony_ci mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & 67762306a36Sopenharmony_ci ~MPIC_VECPRI_MASK); 67862306a36Sopenharmony_ci /* make sure mask gets to controller before we return to user */ 67962306a36Sopenharmony_ci do { 68062306a36Sopenharmony_ci if (!loops--) { 68162306a36Sopenharmony_ci printk(KERN_ERR "%s: timeout on hwirq %u\n", 68262306a36Sopenharmony_ci __func__, src); 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_civoid mpic_mask_irq(struct irq_data *d) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci unsigned int loops = 100000; 69162306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 69262306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 69762306a36Sopenharmony_ci mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | 69862306a36Sopenharmony_ci MPIC_VECPRI_MASK); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* make sure mask gets to controller before we return to user */ 70162306a36Sopenharmony_ci do { 70262306a36Sopenharmony_ci if (!loops--) { 70362306a36Sopenharmony_ci printk(KERN_ERR "%s: timeout on hwirq %u\n", 70462306a36Sopenharmony_ci __func__, src); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_civoid mpic_end_irq(struct irq_data *d) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci#ifdef DEBUG_IRQ 71562306a36Sopenharmony_ci DBG("%s: end_irq: %d\n", mpic->name, d->irq); 71662306a36Sopenharmony_ci#endif 71762306a36Sopenharmony_ci /* We always EOI on end_irq() even for edge interrupts since that 71862306a36Sopenharmony_ci * should only lower the priority, the MPIC should have properly 71962306a36Sopenharmony_ci * latched another edge interrupt coming in anyway 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mpic_eoi(mpic); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void mpic_unmask_ht_irq(struct irq_data *d) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 73062306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci mpic_unmask_irq(d); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (irqd_is_level_type(d)) 73562306a36Sopenharmony_ci mpic_ht_end_irq(mpic, src); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic unsigned int mpic_startup_ht_irq(struct irq_data *d) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 74162306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci mpic_unmask_irq(d); 74462306a36Sopenharmony_ci mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d)); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic void mpic_shutdown_ht_irq(struct irq_data *d) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 75262306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci mpic_shutdown_ht_interrupt(mpic, src); 75562306a36Sopenharmony_ci mpic_mask_irq(d); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void mpic_end_ht_irq(struct irq_data *d) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 76162306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci#ifdef DEBUG_IRQ 76462306a36Sopenharmony_ci DBG("%s: end_irq: %d\n", mpic->name, d->irq); 76562306a36Sopenharmony_ci#endif 76662306a36Sopenharmony_ci /* We always EOI on end_irq() even for edge interrupts since that 76762306a36Sopenharmony_ci * should only lower the priority, the MPIC should have properly 76862306a36Sopenharmony_ci * latched another edge interrupt coming in anyway 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (irqd_is_level_type(d)) 77262306a36Sopenharmony_ci mpic_ht_end_irq(mpic, src); 77362306a36Sopenharmony_ci mpic_eoi(mpic); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci#endif /* !CONFIG_MPIC_U3_HT_IRQS */ 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci#ifdef CONFIG_SMP 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic void mpic_unmask_ipi(struct irq_data *d) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct mpic *mpic = mpic_from_ipi(d); 78262306a36Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0]; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src); 78562306a36Sopenharmony_ci mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void mpic_mask_ipi(struct irq_data *d) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci /* NEVER disable an IPI... that's just plain wrong! */ 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void mpic_end_ipi(struct irq_data *d) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct mpic *mpic = mpic_from_ipi(d); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* 79862306a36Sopenharmony_ci * IPIs are marked IRQ_PER_CPU. This has the side effect of 79962306a36Sopenharmony_ci * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from 80062306a36Sopenharmony_ci * applying to them. We EOI them late to avoid re-entering. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci mpic_eoi(mpic); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void mpic_unmask_tm(struct irq_data *d) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 81062306a36Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src); 81362306a36Sopenharmony_ci mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK); 81462306a36Sopenharmony_ci mpic_tm_read(src); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic void mpic_mask_tm(struct irq_data *d) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 82062306a36Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK); 82362306a36Sopenharmony_ci mpic_tm_read(src); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciint mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, 82762306a36Sopenharmony_ci bool force) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 83062306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (mpic->flags & MPIC_SINGLE_DEST_CPU) { 83362306a36Sopenharmony_ci int cpuid = irq_choose_cpu(cpumask); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 83662306a36Sopenharmony_ci } else { 83762306a36Sopenharmony_ci u32 mask = cpumask_bits(cpumask)[0]; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci mask &= cpumask_bits(cpu_online_mask)[0]; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 84262306a36Sopenharmony_ci mpic_physmask(mask)); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return IRQ_SET_MASK_OK; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci /* Now convert sense value */ 85162306a36Sopenharmony_ci switch(type & IRQ_TYPE_SENSE_MASK) { 85262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 85362306a36Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_EDGE) | 85462306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE); 85562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 85662306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 85762306a36Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_EDGE) | 85862306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 85962306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 86062306a36Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_LEVEL) | 86162306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE); 86262306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 86362306a36Sopenharmony_ci default: 86462306a36Sopenharmony_ci return MPIC_INFO(VECPRI_SENSE_LEVEL) | 86562306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ciint mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq_data(d); 87262306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 87362306a36Sopenharmony_ci unsigned int vecpri, vold, vnew; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", 87662306a36Sopenharmony_ci mpic, d->irq, src, flow_type); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (src >= mpic->num_sources) 87962306a36Sopenharmony_ci return -EINVAL; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* We don't support "none" type */ 88462306a36Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 88562306a36Sopenharmony_ci flow_type = IRQ_TYPE_DEFAULT; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Default: read HW settings */ 88862306a36Sopenharmony_ci if (flow_type == IRQ_TYPE_DEFAULT) { 88962306a36Sopenharmony_ci int vold_ps; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci vold_ps = vold & (MPIC_INFO(VECPRI_POLARITY_MASK) | 89262306a36Sopenharmony_ci MPIC_INFO(VECPRI_SENSE_MASK)); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | 89562306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE))) 89662306a36Sopenharmony_ci flow_type = IRQ_TYPE_EDGE_RISING; 89762306a36Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | 89862306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) 89962306a36Sopenharmony_ci flow_type = IRQ_TYPE_EDGE_FALLING; 90062306a36Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | 90162306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_POSITIVE))) 90262306a36Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_HIGH; 90362306a36Sopenharmony_ci else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | 90462306a36Sopenharmony_ci MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) 90562306a36Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 90662306a36Sopenharmony_ci else 90762306a36Sopenharmony_ci WARN_ONCE(1, "mpic: unknown IRQ type %d\n", vold); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* Apply to irq desc */ 91162306a36Sopenharmony_ci irqd_set_trigger_type(d, flow_type); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Apply to HW */ 91462306a36Sopenharmony_ci if (mpic_is_ht_interrupt(mpic, src)) 91562306a36Sopenharmony_ci vecpri = MPIC_VECPRI_POLARITY_POSITIVE | 91662306a36Sopenharmony_ci MPIC_VECPRI_SENSE_EDGE; 91762306a36Sopenharmony_ci else 91862306a36Sopenharmony_ci vecpri = mpic_type_to_vecpri(mpic, flow_type); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | 92162306a36Sopenharmony_ci MPIC_INFO(VECPRI_SENSE_MASK)); 92262306a36Sopenharmony_ci vnew |= vecpri; 92362306a36Sopenharmony_ci if (vold != vnew) 92462306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return IRQ_SET_MASK_OK_NOCOPY; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_civoid mpic_set_vector(unsigned int virq, unsigned int vector) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq(virq); 93262306a36Sopenharmony_ci unsigned int src = virq_to_hw(virq); 93362306a36Sopenharmony_ci unsigned int vecpri; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", 93662306a36Sopenharmony_ci mpic, virq, src, vector); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (src >= mpic->num_sources) 93962306a36Sopenharmony_ci return; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 94262306a36Sopenharmony_ci vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK); 94362306a36Sopenharmony_ci vecpri |= vector; 94462306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic void mpic_set_destination(unsigned int virq, unsigned int cpuid) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct mpic *mpic = mpic_from_irq(virq); 95062306a36Sopenharmony_ci unsigned int src = virq_to_hw(virq); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n", 95362306a36Sopenharmony_ci mpic, virq, src, cpuid); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (src >= mpic->num_sources) 95662306a36Sopenharmony_ci return; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic struct irq_chip mpic_irq_chip = { 96262306a36Sopenharmony_ci .irq_mask = mpic_mask_irq, 96362306a36Sopenharmony_ci .irq_unmask = mpic_unmask_irq, 96462306a36Sopenharmony_ci .irq_eoi = mpic_end_irq, 96562306a36Sopenharmony_ci .irq_set_type = mpic_set_irq_type, 96662306a36Sopenharmony_ci}; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci#ifdef CONFIG_SMP 96962306a36Sopenharmony_cistatic const struct irq_chip mpic_ipi_chip = { 97062306a36Sopenharmony_ci .irq_mask = mpic_mask_ipi, 97162306a36Sopenharmony_ci .irq_unmask = mpic_unmask_ipi, 97262306a36Sopenharmony_ci .irq_eoi = mpic_end_ipi, 97362306a36Sopenharmony_ci}; 97462306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct irq_chip mpic_tm_chip = { 97762306a36Sopenharmony_ci .irq_mask = mpic_mask_tm, 97862306a36Sopenharmony_ci .irq_unmask = mpic_unmask_tm, 97962306a36Sopenharmony_ci .irq_eoi = mpic_end_irq, 98062306a36Sopenharmony_ci}; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 98362306a36Sopenharmony_cistatic const struct irq_chip mpic_irq_ht_chip = { 98462306a36Sopenharmony_ci .irq_startup = mpic_startup_ht_irq, 98562306a36Sopenharmony_ci .irq_shutdown = mpic_shutdown_ht_irq, 98662306a36Sopenharmony_ci .irq_mask = mpic_mask_irq, 98762306a36Sopenharmony_ci .irq_unmask = mpic_unmask_ht_irq, 98862306a36Sopenharmony_ci .irq_eoi = mpic_end_ht_irq, 98962306a36Sopenharmony_ci .irq_set_type = mpic_set_irq_type, 99062306a36Sopenharmony_ci}; 99162306a36Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int mpic_host_match(struct irq_domain *h, struct device_node *node, 99562306a36Sopenharmony_ci enum irq_domain_bus_token bus_token) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci /* Exact match, unless mpic node is NULL */ 99862306a36Sopenharmony_ci struct device_node *of_node = irq_domain_get_of_node(h); 99962306a36Sopenharmony_ci return of_node == NULL || of_node == node; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int mpic_host_map(struct irq_domain *h, unsigned int virq, 100362306a36Sopenharmony_ci irq_hw_number_t hw) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct mpic *mpic = h->host_data; 100662306a36Sopenharmony_ci struct irq_chip *chip; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (hw == mpic->spurious_vec) 101162306a36Sopenharmony_ci return -EINVAL; 101262306a36Sopenharmony_ci if (mpic->protected && test_bit(hw, mpic->protected)) { 101362306a36Sopenharmony_ci pr_warn("mpic: Mapping of source 0x%x failed, source protected by firmware !\n", 101462306a36Sopenharmony_ci (unsigned int)hw); 101562306a36Sopenharmony_ci return -EPERM; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci#ifdef CONFIG_SMP 101962306a36Sopenharmony_ci else if (hw >= mpic->ipi_vecs[0]) { 102062306a36Sopenharmony_ci WARN_ON(mpic->flags & MPIC_SECONDARY); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci DBG("mpic: mapping as IPI\n"); 102362306a36Sopenharmony_ci irq_set_chip_data(virq, mpic); 102462306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &mpic->hc_ipi, 102562306a36Sopenharmony_ci handle_percpu_irq); 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { 103162306a36Sopenharmony_ci WARN_ON(mpic->flags & MPIC_SECONDARY); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci DBG("mpic: mapping as timer\n"); 103462306a36Sopenharmony_ci irq_set_chip_data(virq, mpic); 103562306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &mpic->hc_tm, 103662306a36Sopenharmony_ci handle_fasteoi_irq); 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (mpic_map_error_int(mpic, virq, hw)) 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (hw >= mpic->num_sources) { 104462306a36Sopenharmony_ci pr_warn("mpic: Mapping of source 0x%x failed, source out of range !\n", 104562306a36Sopenharmony_ci (unsigned int)hw); 104662306a36Sopenharmony_ci return -EINVAL; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci mpic_msi_reserve_hwirq(mpic, hw); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* Default chip */ 105262306a36Sopenharmony_ci chip = &mpic->hc_irq; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 105562306a36Sopenharmony_ci /* Check for HT interrupts, override vecpri */ 105662306a36Sopenharmony_ci if (mpic_is_ht_interrupt(mpic, hw)) 105762306a36Sopenharmony_ci chip = &mpic->hc_ht_irq; 105862306a36Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci DBG("mpic: mapping to irq chip @%p\n", chip); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci irq_set_chip_data(virq, mpic); 106362306a36Sopenharmony_ci irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* Set default irq type */ 106662306a36Sopenharmony_ci irq_set_irq_type(virq, IRQ_TYPE_DEFAULT); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* If the MPIC was reset, then all vectors have already been 106962306a36Sopenharmony_ci * initialized. Otherwise, a per source lazy initialization 107062306a36Sopenharmony_ci * is done here. 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ci if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) { 107362306a36Sopenharmony_ci int cpu; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci preempt_disable(); 107662306a36Sopenharmony_ci cpu = mpic_processor_id(mpic); 107762306a36Sopenharmony_ci preempt_enable(); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci mpic_set_vector(virq, hw); 108062306a36Sopenharmony_ci mpic_set_destination(virq, cpu); 108162306a36Sopenharmony_ci mpic_irq_set_priority(virq, 8); 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic int mpic_host_xlate(struct irq_domain *h, struct device_node *ct, 108862306a36Sopenharmony_ci const u32 *intspec, unsigned int intsize, 108962306a36Sopenharmony_ci irq_hw_number_t *out_hwirq, unsigned int *out_flags) 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct mpic *mpic = h->host_data; 109362306a36Sopenharmony_ci static unsigned char map_mpic_senses[4] = { 109462306a36Sopenharmony_ci IRQ_TYPE_EDGE_RISING, 109562306a36Sopenharmony_ci IRQ_TYPE_LEVEL_LOW, 109662306a36Sopenharmony_ci IRQ_TYPE_LEVEL_HIGH, 109762306a36Sopenharmony_ci IRQ_TYPE_EDGE_FALLING, 109862306a36Sopenharmony_ci }; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci *out_hwirq = intspec[0]; 110162306a36Sopenharmony_ci if (intsize >= 4 && (mpic->flags & MPIC_FSL)) { 110262306a36Sopenharmony_ci /* 110362306a36Sopenharmony_ci * Freescale MPIC with extended intspec: 110462306a36Sopenharmony_ci * First two cells are as usual. Third specifies 110562306a36Sopenharmony_ci * an "interrupt type". Fourth is type-specific data. 110662306a36Sopenharmony_ci * 110762306a36Sopenharmony_ci * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci switch (intspec[2]) { 111062306a36Sopenharmony_ci case 0: 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci case 1: 111362306a36Sopenharmony_ci if (!(mpic->flags & MPIC_FSL_HAS_EIMR)) 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs)) 111762306a36Sopenharmony_ci return -EINVAL; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci *out_hwirq = mpic->err_int_vecs[intspec[3]]; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci break; 112262306a36Sopenharmony_ci case 2: 112362306a36Sopenharmony_ci if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) 112462306a36Sopenharmony_ci return -EINVAL; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci *out_hwirq = mpic->ipi_vecs[intspec[0]]; 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci case 3: 112962306a36Sopenharmony_ci if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs)) 113062306a36Sopenharmony_ci return -EINVAL; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci *out_hwirq = mpic->timer_vecs[intspec[0]]; 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci default: 113562306a36Sopenharmony_ci pr_debug("%s: unknown irq type %u\n", 113662306a36Sopenharmony_ci __func__, intspec[2]); 113762306a36Sopenharmony_ci return -EINVAL; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci *out_flags = map_mpic_senses[intspec[1] & 3]; 114162306a36Sopenharmony_ci } else if (intsize > 1) { 114262306a36Sopenharmony_ci u32 mask = 0x3; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* Apple invented a new race of encoding on machines with 114562306a36Sopenharmony_ci * an HT APIC. They encode, among others, the index within 114662306a36Sopenharmony_ci * the HT APIC. We don't care about it here since thankfully, 114762306a36Sopenharmony_ci * it appears that they have the APIC already properly 114862306a36Sopenharmony_ci * configured, and thus our current fixup code that reads the 114962306a36Sopenharmony_ci * APIC config works fine. However, we still need to mask out 115062306a36Sopenharmony_ci * bits in the specifier to make sure we only get bit 0 which 115162306a36Sopenharmony_ci * is the level/edge bit (the only sense bit exposed by Apple), 115262306a36Sopenharmony_ci * as their bit 1 means something else. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci if (machine_is(powermac)) 115562306a36Sopenharmony_ci mask = 0x1; 115662306a36Sopenharmony_ci *out_flags = map_mpic_senses[intspec[1] & mask]; 115762306a36Sopenharmony_ci } else 115862306a36Sopenharmony_ci *out_flags = IRQ_TYPE_NONE; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n", 116162306a36Sopenharmony_ci intsize, intspec[0], intspec[1], *out_hwirq, *out_flags); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return 0; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/* IRQ handler for a secondary MPIC cascaded from another IRQ controller */ 116762306a36Sopenharmony_cistatic void mpic_cascade(struct irq_desc *desc) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 117062306a36Sopenharmony_ci struct mpic *mpic = irq_desc_get_handler_data(desc); 117162306a36Sopenharmony_ci unsigned int virq; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci BUG_ON(!(mpic->flags & MPIC_SECONDARY)); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci virq = mpic_get_one_irq(mpic); 117662306a36Sopenharmony_ci if (virq) 117762306a36Sopenharmony_ci generic_handle_irq(virq); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci chip->irq_eoi(&desc->irq_data); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic const struct irq_domain_ops mpic_host_ops = { 118362306a36Sopenharmony_ci .match = mpic_host_match, 118462306a36Sopenharmony_ci .map = mpic_host_map, 118562306a36Sopenharmony_ci .xlate = mpic_host_xlate, 118662306a36Sopenharmony_ci}; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic u32 fsl_mpic_get_version(struct mpic *mpic) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci u32 brr1; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (!(mpic->flags & MPIC_FSL)) 119362306a36Sopenharmony_ci return 0; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, 119662306a36Sopenharmony_ci MPIC_FSL_BRR1); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci return brr1 & MPIC_FSL_BRR1_VER; 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci/* 120262306a36Sopenharmony_ci * Exported functions 120362306a36Sopenharmony_ci */ 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ciu32 fsl_mpic_primary_get_version(void) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (mpic) 121062306a36Sopenharmony_ci return fsl_mpic_get_version(mpic); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return 0; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistruct mpic * __init mpic_alloc(struct device_node *node, 121662306a36Sopenharmony_ci phys_addr_t phys_addr, 121762306a36Sopenharmony_ci unsigned int flags, 121862306a36Sopenharmony_ci unsigned int isu_size, 121962306a36Sopenharmony_ci unsigned int irq_count, 122062306a36Sopenharmony_ci const char *name) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci int i, psize, intvec_top; 122362306a36Sopenharmony_ci struct mpic *mpic; 122462306a36Sopenharmony_ci u32 greg_feature; 122562306a36Sopenharmony_ci const char *vers; 122662306a36Sopenharmony_ci const u32 *psrc; 122762306a36Sopenharmony_ci u32 last_irq; 122862306a36Sopenharmony_ci u32 fsl_version = 0; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Default MPIC search parameters */ 123162306a36Sopenharmony_ci static const struct of_device_id __initconst mpic_device_id[] = { 123262306a36Sopenharmony_ci { .type = "open-pic", }, 123362306a36Sopenharmony_ci { .compatible = "open-pic", }, 123462306a36Sopenharmony_ci {}, 123562306a36Sopenharmony_ci }; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * If we were not passed a device-tree node, then perform the default 123962306a36Sopenharmony_ci * search for standardized a standardized OpenPIC. 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci if (node) { 124262306a36Sopenharmony_ci node = of_node_get(node); 124362306a36Sopenharmony_ci } else { 124462306a36Sopenharmony_ci node = of_find_matching_node(NULL, mpic_device_id); 124562306a36Sopenharmony_ci if (!node) 124662306a36Sopenharmony_ci return NULL; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* Pick the physical address from the device tree if unspecified */ 125062306a36Sopenharmony_ci if (!phys_addr) { 125162306a36Sopenharmony_ci /* Check if it is DCR-based */ 125262306a36Sopenharmony_ci if (of_property_read_bool(node, "dcr-reg")) { 125362306a36Sopenharmony_ci flags |= MPIC_USES_DCR; 125462306a36Sopenharmony_ci } else { 125562306a36Sopenharmony_ci struct resource r; 125662306a36Sopenharmony_ci if (of_address_to_resource(node, 0, &r)) 125762306a36Sopenharmony_ci goto err_of_node_put; 125862306a36Sopenharmony_ci phys_addr = r.start; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* Read extra device-tree properties into the flags variable */ 126362306a36Sopenharmony_ci if (of_property_read_bool(node, "big-endian")) 126462306a36Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 126562306a36Sopenharmony_ci if (of_property_read_bool(node, "pic-no-reset")) 126662306a36Sopenharmony_ci flags |= MPIC_NO_RESET; 126762306a36Sopenharmony_ci if (of_property_read_bool(node, "single-cpu-affinity")) 126862306a36Sopenharmony_ci flags |= MPIC_SINGLE_DEST_CPU; 126962306a36Sopenharmony_ci if (of_device_is_compatible(node, "fsl,mpic")) { 127062306a36Sopenharmony_ci flags |= MPIC_FSL | MPIC_LARGE_VECTORS; 127162306a36Sopenharmony_ci mpic_irq_chip.flags |= IRQCHIP_SKIP_SET_WAKE; 127262306a36Sopenharmony_ci mpic_tm_chip.flags |= IRQCHIP_SKIP_SET_WAKE; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL); 127662306a36Sopenharmony_ci if (mpic == NULL) 127762306a36Sopenharmony_ci goto err_of_node_put; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci mpic->name = name; 128062306a36Sopenharmony_ci mpic->node = node; 128162306a36Sopenharmony_ci mpic->paddr = phys_addr; 128262306a36Sopenharmony_ci mpic->flags = flags; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci mpic->hc_irq = mpic_irq_chip; 128562306a36Sopenharmony_ci mpic->hc_irq.name = name; 128662306a36Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 128762306a36Sopenharmony_ci mpic->hc_irq.irq_set_affinity = mpic_set_affinity; 128862306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 128962306a36Sopenharmony_ci mpic->hc_ht_irq = mpic_irq_ht_chip; 129062306a36Sopenharmony_ci mpic->hc_ht_irq.name = name; 129162306a36Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) 129262306a36Sopenharmony_ci mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity; 129362306a36Sopenharmony_ci#endif /* CONFIG_MPIC_U3_HT_IRQS */ 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci#ifdef CONFIG_SMP 129662306a36Sopenharmony_ci mpic->hc_ipi = mpic_ipi_chip; 129762306a36Sopenharmony_ci mpic->hc_ipi.name = name; 129862306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci mpic->hc_tm = mpic_tm_chip; 130162306a36Sopenharmony_ci mpic->hc_tm.name = name; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci mpic->num_sources = 0; /* so far */ 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci if (mpic->flags & MPIC_LARGE_VECTORS) 130662306a36Sopenharmony_ci intvec_top = 2047; 130762306a36Sopenharmony_ci else 130862306a36Sopenharmony_ci intvec_top = 255; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci mpic->timer_vecs[0] = intvec_top - 12; 131162306a36Sopenharmony_ci mpic->timer_vecs[1] = intvec_top - 11; 131262306a36Sopenharmony_ci mpic->timer_vecs[2] = intvec_top - 10; 131362306a36Sopenharmony_ci mpic->timer_vecs[3] = intvec_top - 9; 131462306a36Sopenharmony_ci mpic->timer_vecs[4] = intvec_top - 8; 131562306a36Sopenharmony_ci mpic->timer_vecs[5] = intvec_top - 7; 131662306a36Sopenharmony_ci mpic->timer_vecs[6] = intvec_top - 6; 131762306a36Sopenharmony_ci mpic->timer_vecs[7] = intvec_top - 5; 131862306a36Sopenharmony_ci mpic->ipi_vecs[0] = intvec_top - 4; 131962306a36Sopenharmony_ci mpic->ipi_vecs[1] = intvec_top - 3; 132062306a36Sopenharmony_ci mpic->ipi_vecs[2] = intvec_top - 2; 132162306a36Sopenharmony_ci mpic->ipi_vecs[3] = intvec_top - 1; 132262306a36Sopenharmony_ci mpic->spurious_vec = intvec_top; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Look for protected sources */ 132562306a36Sopenharmony_ci psrc = of_get_property(mpic->node, "protected-sources", &psize); 132662306a36Sopenharmony_ci if (psrc) { 132762306a36Sopenharmony_ci /* Allocate a bitmap with one bit per interrupt */ 132862306a36Sopenharmony_ci mpic->protected = bitmap_zalloc(intvec_top + 1, GFP_KERNEL); 132962306a36Sopenharmony_ci BUG_ON(mpic->protected == NULL); 133062306a36Sopenharmony_ci for (i = 0; i < psize/sizeof(u32); i++) { 133162306a36Sopenharmony_ci if (psrc[i] > intvec_top) 133262306a36Sopenharmony_ci continue; 133362306a36Sopenharmony_ci __set_bit(psrc[i], mpic->protected); 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci#ifdef CONFIG_MPIC_WEIRD 133862306a36Sopenharmony_ci mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)]; 133962306a36Sopenharmony_ci#endif 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* default register type */ 134262306a36Sopenharmony_ci if (mpic->flags & MPIC_BIG_ENDIAN) 134362306a36Sopenharmony_ci mpic->reg_type = mpic_access_mmio_be; 134462306a36Sopenharmony_ci else 134562306a36Sopenharmony_ci mpic->reg_type = mpic_access_mmio_le; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci /* 134862306a36Sopenharmony_ci * An MPIC with a "dcr-reg" property must be accessed that way, but 134962306a36Sopenharmony_ci * only if the kernel includes DCR support. 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR 135262306a36Sopenharmony_ci if (mpic->flags & MPIC_USES_DCR) 135362306a36Sopenharmony_ci mpic->reg_type = mpic_access_dcr; 135462306a36Sopenharmony_ci#else 135562306a36Sopenharmony_ci BUG_ON(mpic->flags & MPIC_USES_DCR); 135662306a36Sopenharmony_ci#endif 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* Map the global registers */ 135962306a36Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); 136062306a36Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 136362306a36Sopenharmony_ci int ret; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* 136662306a36Sopenharmony_ci * Yes, Freescale really did put global registers in the 136762306a36Sopenharmony_ci * magic per-cpu area -- and they don't even show up in the 136862306a36Sopenharmony_ci * non-magic per-cpu copies that this driver normally uses. 136962306a36Sopenharmony_ci */ 137062306a36Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, 137162306a36Sopenharmony_ci MPIC_CPU_THISBASE, 0x1000); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci fsl_version = fsl_mpic_get_version(mpic); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* Error interrupt mask register (EIMR) is required for 137662306a36Sopenharmony_ci * handling individual device error interrupts. EIMR 137762306a36Sopenharmony_ci * was added in MPIC version 4.1. 137862306a36Sopenharmony_ci * 137962306a36Sopenharmony_ci * Over here we reserve vector number space for error 138062306a36Sopenharmony_ci * interrupt vectors. This space is stolen from the 138162306a36Sopenharmony_ci * global vector number space, as in case of ipis 138262306a36Sopenharmony_ci * and timer interrupts. 138362306a36Sopenharmony_ci * 138462306a36Sopenharmony_ci * Available vector space = intvec_top - 13, where 13 138562306a36Sopenharmony_ci * is the number of vectors which have been consumed by 138662306a36Sopenharmony_ci * ipis, timer interrupts and spurious. 138762306a36Sopenharmony_ci */ 138862306a36Sopenharmony_ci if (fsl_version >= 0x401) { 138962306a36Sopenharmony_ci ret = mpic_setup_error_int(mpic, intvec_top - 13); 139062306a36Sopenharmony_ci if (ret) 139162306a36Sopenharmony_ci return NULL; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* 139762306a36Sopenharmony_ci * EPR is only available starting with v4.0. To support 139862306a36Sopenharmony_ci * platforms that don't know the MPIC version at compile-time, 139962306a36Sopenharmony_ci * such as qemu-e500, turn off coreint if this MPIC doesn't 140062306a36Sopenharmony_ci * support it. Note that we never enable it if it wasn't 140162306a36Sopenharmony_ci * requested in the first place. 140262306a36Sopenharmony_ci * 140362306a36Sopenharmony_ci * This is done outside the MPIC_FSL check, so that we 140462306a36Sopenharmony_ci * also disable coreint if the MPIC node doesn't have 140562306a36Sopenharmony_ci * an "fsl,mpic" compatible at all. This will be the case 140662306a36Sopenharmony_ci * with device trees generated by older versions of QEMU. 140762306a36Sopenharmony_ci * fsl_version will be zero if MPIC_FSL is not set. 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_ci if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) 141062306a36Sopenharmony_ci ppc_md.get_irq = mpic_get_irq; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* Reset */ 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* When using a device-node, reset requests are only honored if the MPIC 141562306a36Sopenharmony_ci * is allowed to reset. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci if (!(mpic->flags & MPIC_NO_RESET)) { 141862306a36Sopenharmony_ci printk(KERN_DEBUG "mpic: Resetting\n"); 141962306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 142062306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 142162306a36Sopenharmony_ci | MPIC_GREG_GCONF_RESET); 142262306a36Sopenharmony_ci while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 142362306a36Sopenharmony_ci & MPIC_GREG_GCONF_RESET) 142462306a36Sopenharmony_ci mb(); 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* CoreInt */ 142862306a36Sopenharmony_ci if (mpic->flags & MPIC_ENABLE_COREINT) 142962306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 143062306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 143162306a36Sopenharmony_ci | MPIC_GREG_GCONF_COREINT); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (mpic->flags & MPIC_ENABLE_MCK) 143462306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 143562306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 143662306a36Sopenharmony_ci | MPIC_GREG_GCONF_MCK); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* 143962306a36Sopenharmony_ci * The MPIC driver will crash if there are more cores than we 144062306a36Sopenharmony_ci * can initialize, so we may as well catch that problem here. 144162306a36Sopenharmony_ci */ 144262306a36Sopenharmony_ci BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* Map the per-CPU registers */ 144562306a36Sopenharmony_ci for_each_possible_cpu(i) { 144662306a36Sopenharmony_ci unsigned int cpu = get_hard_smp_processor_id(i); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu], 144962306a36Sopenharmony_ci MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 145062306a36Sopenharmony_ci 0x1000); 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* 145462306a36Sopenharmony_ci * Read feature register. For non-ISU MPICs, num sources as well. On 145562306a36Sopenharmony_ci * ISU MPICs, sources are counted as ISUs are added 145662306a36Sopenharmony_ci */ 145762306a36Sopenharmony_ci greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* 146062306a36Sopenharmony_ci * By default, the last source number comes from the MPIC, but the 146162306a36Sopenharmony_ci * device-tree and board support code can override it on buggy hw. 146262306a36Sopenharmony_ci * If we get passed an isu_size (multi-isu MPIC) then we use that 146362306a36Sopenharmony_ci * as a default instead of the value read from the HW. 146462306a36Sopenharmony_ci */ 146562306a36Sopenharmony_ci last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) 146662306a36Sopenharmony_ci >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; 146762306a36Sopenharmony_ci if (isu_size) 146862306a36Sopenharmony_ci last_irq = isu_size * MPIC_MAX_ISU - 1; 146962306a36Sopenharmony_ci of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); 147062306a36Sopenharmony_ci if (irq_count) 147162306a36Sopenharmony_ci last_irq = irq_count - 1; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci /* Initialize main ISU if none provided */ 147462306a36Sopenharmony_ci if (!isu_size) { 147562306a36Sopenharmony_ci isu_size = last_irq + 1; 147662306a36Sopenharmony_ci mpic->num_sources = isu_size; 147762306a36Sopenharmony_ci mpic_map(mpic, mpic->paddr, &mpic->isus[0], 147862306a36Sopenharmony_ci MPIC_INFO(IRQ_BASE), 147962306a36Sopenharmony_ci MPIC_INFO(IRQ_STRIDE) * isu_size); 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci mpic->isu_size = isu_size; 148362306a36Sopenharmony_ci mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); 148462306a36Sopenharmony_ci mpic->isu_mask = (1 << mpic->isu_shift) - 1; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci mpic->irqhost = irq_domain_add_linear(mpic->node, 148762306a36Sopenharmony_ci intvec_top, 148862306a36Sopenharmony_ci &mpic_host_ops, mpic); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* 149162306a36Sopenharmony_ci * FIXME: The code leaks the MPIC object and mappings here; this 149262306a36Sopenharmony_ci * is very unlikely to fail but it ought to be fixed anyways. 149362306a36Sopenharmony_ci */ 149462306a36Sopenharmony_ci if (mpic->irqhost == NULL) 149562306a36Sopenharmony_ci return NULL; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci /* Display version */ 149862306a36Sopenharmony_ci switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) { 149962306a36Sopenharmony_ci case 1: 150062306a36Sopenharmony_ci vers = "1.0"; 150162306a36Sopenharmony_ci break; 150262306a36Sopenharmony_ci case 2: 150362306a36Sopenharmony_ci vers = "1.2"; 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci case 3: 150662306a36Sopenharmony_ci vers = "1.3"; 150762306a36Sopenharmony_ci break; 150862306a36Sopenharmony_ci default: 150962306a36Sopenharmony_ci vers = "<unknown>"; 151062306a36Sopenharmony_ci break; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," 151362306a36Sopenharmony_ci " max %d CPUs\n", 151462306a36Sopenharmony_ci name, vers, (unsigned long long)mpic->paddr, num_possible_cpus()); 151562306a36Sopenharmony_ci printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", 151662306a36Sopenharmony_ci mpic->isu_size, mpic->isu_shift, mpic->isu_mask); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci mpic->next = mpics; 151962306a36Sopenharmony_ci mpics = mpic; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (!(mpic->flags & MPIC_SECONDARY)) { 152262306a36Sopenharmony_ci mpic_primary = mpic; 152362306a36Sopenharmony_ci irq_set_default_host(mpic->irqhost); 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci return mpic; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cierr_of_node_put: 152962306a36Sopenharmony_ci of_node_put(node); 153062306a36Sopenharmony_ci return NULL; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_civoid __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, 153462306a36Sopenharmony_ci phys_addr_t paddr) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci unsigned int isu_first = isu_num * mpic->isu_size; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci BUG_ON(isu_num >= MPIC_MAX_ISU); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci mpic_map(mpic, 154162306a36Sopenharmony_ci paddr, &mpic->isus[isu_num], 0, 154262306a36Sopenharmony_ci MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci if ((isu_first + mpic->isu_size) > mpic->num_sources) 154562306a36Sopenharmony_ci mpic->num_sources = isu_first + mpic->isu_size; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_civoid __init mpic_init(struct mpic *mpic) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci int i, cpu; 155162306a36Sopenharmony_ci int num_timers = 4; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci BUG_ON(mpic->num_sources == 0); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci /* Set current processor priority to max */ 155862306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 156162306a36Sopenharmony_ci u32 version = fsl_mpic_get_version(mpic); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci /* 156462306a36Sopenharmony_ci * Timer group B is present at the latest in MPIC 3.1 (e.g. 156562306a36Sopenharmony_ci * mpc8536). It is not present in MPIC 2.0 (e.g. mpc8544). 156662306a36Sopenharmony_ci * I don't know about the status of intermediate versions (or 156762306a36Sopenharmony_ci * whether they even exist). 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_ci if (version >= 0x0301) 157062306a36Sopenharmony_ci num_timers = 8; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci /* Initialize timers to our reserved vectors and mask them for now */ 157462306a36Sopenharmony_ci for (i = 0; i < num_timers; i++) { 157562306a36Sopenharmony_ci unsigned int offset = mpic_tm_offset(mpic, i); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci mpic_write(mpic->tmregs, 157862306a36Sopenharmony_ci offset + MPIC_INFO(TIMER_DESTINATION), 157962306a36Sopenharmony_ci 1 << hard_smp_processor_id()); 158062306a36Sopenharmony_ci mpic_write(mpic->tmregs, 158162306a36Sopenharmony_ci offset + MPIC_INFO(TIMER_VECTOR_PRI), 158262306a36Sopenharmony_ci MPIC_VECPRI_MASK | 158362306a36Sopenharmony_ci (9 << MPIC_VECPRI_PRIORITY_SHIFT) | 158462306a36Sopenharmony_ci (mpic->timer_vecs[0] + i)); 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci /* Initialize IPIs to our reserved vectors and mark them disabled for now */ 158862306a36Sopenharmony_ci mpic_test_broken_ipi(mpic); 158962306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 159062306a36Sopenharmony_ci mpic_ipi_write(i, 159162306a36Sopenharmony_ci MPIC_VECPRI_MASK | 159262306a36Sopenharmony_ci (10 << MPIC_VECPRI_PRIORITY_SHIFT) | 159362306a36Sopenharmony_ci (mpic->ipi_vecs[0] + i)); 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* Do the HT PIC fixups on U3 broken mpic */ 159762306a36Sopenharmony_ci DBG("MPIC flags: %x\n", mpic->flags); 159862306a36Sopenharmony_ci if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) { 159962306a36Sopenharmony_ci mpic_scan_ht_pics(mpic); 160062306a36Sopenharmony_ci mpic_u3msi_init(mpic); 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci mpic_pasemi_msi_init(mpic); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci cpu = mpic_processor_id(mpic); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (!(mpic->flags & MPIC_NO_RESET)) { 160862306a36Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 160962306a36Sopenharmony_ci /* start with vector = source number, and masked */ 161062306a36Sopenharmony_ci u32 vecpri = MPIC_VECPRI_MASK | i | 161162306a36Sopenharmony_ci (8 << MPIC_VECPRI_PRIORITY_SHIFT); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* check if protected */ 161462306a36Sopenharmony_ci if (mpic->protected && test_bit(i, mpic->protected)) 161562306a36Sopenharmony_ci continue; 161662306a36Sopenharmony_ci /* init hw */ 161762306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 161862306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* Init spurious vector */ 162362306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci /* Disable 8259 passthrough, if supported */ 162662306a36Sopenharmony_ci if (!(mpic->flags & MPIC_NO_PTHROU_DIS)) 162762306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 162862306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 162962306a36Sopenharmony_ci | MPIC_GREG_GCONF_8259_PTHROU_DIS); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci if (mpic->flags & MPIC_NO_BIAS) 163262306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 163362306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 163462306a36Sopenharmony_ci | MPIC_GREG_GCONF_NO_BIAS); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* Set current processor priority to 0 */ 163762306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci#ifdef CONFIG_PM 164062306a36Sopenharmony_ci /* allocate memory to save mpic state */ 164162306a36Sopenharmony_ci mpic->save_data = kmalloc_array(mpic->num_sources, 164262306a36Sopenharmony_ci sizeof(*mpic->save_data), 164362306a36Sopenharmony_ci GFP_KERNEL); 164462306a36Sopenharmony_ci BUG_ON(mpic->save_data == NULL); 164562306a36Sopenharmony_ci#endif 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci /* Check if this MPIC is chained from a parent interrupt controller */ 164862306a36Sopenharmony_ci if (mpic->flags & MPIC_SECONDARY) { 164962306a36Sopenharmony_ci int virq = irq_of_parse_and_map(mpic->node, 0); 165062306a36Sopenharmony_ci if (virq) { 165162306a36Sopenharmony_ci printk(KERN_INFO "%pOF: hooking up to IRQ %d\n", 165262306a36Sopenharmony_ci mpic->node, virq); 165362306a36Sopenharmony_ci irq_set_handler_data(virq, mpic); 165462306a36Sopenharmony_ci irq_set_chained_handler(virq, &mpic_cascade); 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* FSL mpic error interrupt initialization */ 165962306a36Sopenharmony_ci if (mpic->flags & MPIC_FSL_HAS_EIMR) 166062306a36Sopenharmony_ci mpic_err_int_init(mpic, MPIC_FSL_ERR_INT); 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_civoid mpic_irq_set_priority(unsigned int irq, unsigned int pri) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct mpic *mpic = mpic_find(irq); 166662306a36Sopenharmony_ci unsigned int src = virq_to_hw(irq); 166762306a36Sopenharmony_ci unsigned long flags; 166862306a36Sopenharmony_ci u32 reg; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (!mpic) 167162306a36Sopenharmony_ci return; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 167462306a36Sopenharmony_ci if (mpic_is_ipi(mpic, src)) { 167562306a36Sopenharmony_ci reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & 167662306a36Sopenharmony_ci ~MPIC_VECPRI_PRIORITY_MASK; 167762306a36Sopenharmony_ci mpic_ipi_write(src - mpic->ipi_vecs[0], 167862306a36Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 167962306a36Sopenharmony_ci } else if (mpic_is_tm(mpic, src)) { 168062306a36Sopenharmony_ci reg = mpic_tm_read(src - mpic->timer_vecs[0]) & 168162306a36Sopenharmony_ci ~MPIC_VECPRI_PRIORITY_MASK; 168262306a36Sopenharmony_ci mpic_tm_write(src - mpic->timer_vecs[0], 168362306a36Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 168462306a36Sopenharmony_ci } else { 168562306a36Sopenharmony_ci reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) 168662306a36Sopenharmony_ci & ~MPIC_VECPRI_PRIORITY_MASK; 168762306a36Sopenharmony_ci mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 168862306a36Sopenharmony_ci reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 169162306a36Sopenharmony_ci} 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_civoid mpic_setup_this_cpu(void) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci#ifdef CONFIG_SMP 169662306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 169762306a36Sopenharmony_ci unsigned long flags; 169862306a36Sopenharmony_ci u32 msk = 1 << hard_smp_processor_id(); 169962306a36Sopenharmony_ci unsigned int i; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci BUG_ON(mpic == NULL); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci /* let the mpic know we want intrs. default affinity is 0xffffffff 170862306a36Sopenharmony_ci * until changed via /proc. That's how it's done on x86. If we want 170962306a36Sopenharmony_ci * it differently, then we should make sure we also change the default 171062306a36Sopenharmony_ci * values of irq_desc[].affinity in irq.c. 171162306a36Sopenharmony_ci */ 171262306a36Sopenharmony_ci if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) { 171362306a36Sopenharmony_ci for (i = 0; i < mpic->num_sources ; i++) 171462306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 171562306a36Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk); 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci /* Set current processor priority to 0 */ 171962306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 172262306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ciint mpic_cpu_get_priority(void) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI)); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_civoid mpic_cpu_set_priority(int prio) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci prio &= MPIC_CPU_TASKPRI_MASK; 173762306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio); 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_civoid mpic_teardown_this_cpu(int secondary) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 174362306a36Sopenharmony_ci unsigned long flags; 174462306a36Sopenharmony_ci u32 msk = 1 << hard_smp_processor_id(); 174562306a36Sopenharmony_ci unsigned int i; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci BUG_ON(mpic == NULL); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 175062306a36Sopenharmony_ci raw_spin_lock_irqsave(&mpic_lock, flags); 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci /* let the mpic know we don't want intrs. */ 175362306a36Sopenharmony_ci for (i = 0; i < mpic->num_sources ; i++) 175462306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 175562306a36Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* Set current processor priority to max */ 175862306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 175962306a36Sopenharmony_ci /* We need to EOI the IPI since not all platforms reset the MPIC 176062306a36Sopenharmony_ci * on boot and new interrupts wouldn't get delivered otherwise. 176162306a36Sopenharmony_ci */ 176262306a36Sopenharmony_ci mpic_eoi(mpic); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&mpic_lock, flags); 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci u32 src; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci src = mpic_cpu_read(reg) & MPIC_INFO(VECPRI_VECTOR_MASK); 177362306a36Sopenharmony_ci#ifdef DEBUG_LOW 177462306a36Sopenharmony_ci DBG("%s: get_one_irq(reg 0x%x): %d\n", mpic->name, reg, src); 177562306a36Sopenharmony_ci#endif 177662306a36Sopenharmony_ci if (unlikely(src == mpic->spurious_vec)) { 177762306a36Sopenharmony_ci if (mpic->flags & MPIC_SPV_EOI) 177862306a36Sopenharmony_ci mpic_eoi(mpic); 177962306a36Sopenharmony_ci return 0; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 178262306a36Sopenharmony_ci printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 178362306a36Sopenharmony_ci mpic->name, (int)src); 178462306a36Sopenharmony_ci mpic_eoi(mpic); 178562306a36Sopenharmony_ci return 0; 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci return irq_linear_revmap(mpic->irqhost, src); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ciunsigned int mpic_get_one_irq(struct mpic *mpic) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_INTACK)); 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ciunsigned int mpic_get_irq(void) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci BUG_ON(mpic == NULL); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci return mpic_get_one_irq(mpic); 180362306a36Sopenharmony_ci} 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ciunsigned int mpic_get_coreint_irq(void) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci#ifdef CONFIG_BOOKE 180862306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 180962306a36Sopenharmony_ci u32 src; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci BUG_ON(mpic == NULL); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci src = mfspr(SPRN_EPR); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci if (unlikely(src == mpic->spurious_vec)) { 181662306a36Sopenharmony_ci if (mpic->flags & MPIC_SPV_EOI) 181762306a36Sopenharmony_ci mpic_eoi(mpic); 181862306a36Sopenharmony_ci return 0; 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 182162306a36Sopenharmony_ci printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 182262306a36Sopenharmony_ci mpic->name, (int)src); 182362306a36Sopenharmony_ci return 0; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci return irq_linear_revmap(mpic->irqhost, src); 182762306a36Sopenharmony_ci#else 182862306a36Sopenharmony_ci return 0; 182962306a36Sopenharmony_ci#endif 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ciunsigned int mpic_get_mcirq(void) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci BUG_ON(mpic == NULL); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_MCACK)); 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci#ifdef CONFIG_SMP 184262306a36Sopenharmony_civoid __init mpic_request_ipis(void) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 184562306a36Sopenharmony_ci int i; 184662306a36Sopenharmony_ci BUG_ON(mpic == NULL); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci printk(KERN_INFO "mpic: requesting IPIs...\n"); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 185162306a36Sopenharmony_ci unsigned int vipi = irq_create_mapping(mpic->irqhost, 185262306a36Sopenharmony_ci mpic->ipi_vecs[0] + i); 185362306a36Sopenharmony_ci if (!vipi) { 185462306a36Sopenharmony_ci printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]); 185562306a36Sopenharmony_ci continue; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci smp_request_message_ipi(vipi, i); 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_civoid smp_mpic_message_pass(int cpu, int msg) 186262306a36Sopenharmony_ci{ 186362306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 186462306a36Sopenharmony_ci u32 physmask; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci BUG_ON(mpic == NULL); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* make sure we're sending something that translates to an IPI */ 186962306a36Sopenharmony_ci if ((unsigned int)msg > 3) { 187062306a36Sopenharmony_ci printk("SMP %d: smp_message_pass: unknown msg %d\n", 187162306a36Sopenharmony_ci smp_processor_id(), msg); 187262306a36Sopenharmony_ci return; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci#ifdef DEBUG_IPI 187662306a36Sopenharmony_ci DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg); 187762306a36Sopenharmony_ci#endif 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci physmask = 1 << get_hard_smp_processor_id(cpu); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + 188262306a36Sopenharmony_ci msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask); 188362306a36Sopenharmony_ci} 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_civoid __init smp_mpic_probe(void) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci int nr_cpus; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci DBG("smp_mpic_probe()...\n"); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci nr_cpus = num_possible_cpus(); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci DBG("nr_cpus: %d\n", nr_cpus); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci if (nr_cpus > 1) 189662306a36Sopenharmony_ci mpic_request_ipis(); 189762306a36Sopenharmony_ci} 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_civoid smp_mpic_setup_cpu(int cpu) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci mpic_setup_this_cpu(); 190262306a36Sopenharmony_ci} 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_civoid mpic_reset_core(int cpu) 190562306a36Sopenharmony_ci{ 190662306a36Sopenharmony_ci struct mpic *mpic = mpic_primary; 190762306a36Sopenharmony_ci u32 pir; 190862306a36Sopenharmony_ci int cpuid = get_hard_smp_processor_id(cpu); 190962306a36Sopenharmony_ci int i; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* Set target bit for core reset */ 191262306a36Sopenharmony_ci pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 191362306a36Sopenharmony_ci pir |= (1 << cpuid); 191462306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 191562306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* Restore target bit after reset complete */ 191862306a36Sopenharmony_ci pir &= ~(1 << cpuid); 191962306a36Sopenharmony_ci mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 192062306a36Sopenharmony_ci mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* Perform 15 EOI on each reset core to clear pending interrupts. 192362306a36Sopenharmony_ci * This is required for FSL CoreNet based devices */ 192462306a36Sopenharmony_ci if (mpic->flags & MPIC_FSL) { 192562306a36Sopenharmony_ci for (i = 0; i < 15; i++) { 192662306a36Sopenharmony_ci _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid], 192762306a36Sopenharmony_ci MPIC_CPU_EOI, 0); 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci#ifdef CONFIG_PM 193462306a36Sopenharmony_cistatic void mpic_suspend_one(struct mpic *mpic) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci int i; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 193962306a36Sopenharmony_ci mpic->save_data[i].vecprio = 194062306a36Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); 194162306a36Sopenharmony_ci mpic->save_data[i].dest = 194262306a36Sopenharmony_ci mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci} 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_cistatic int mpic_suspend(void) 194762306a36Sopenharmony_ci{ 194862306a36Sopenharmony_ci struct mpic *mpic = mpics; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci while (mpic) { 195162306a36Sopenharmony_ci mpic_suspend_one(mpic); 195262306a36Sopenharmony_ci mpic = mpic->next; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci return 0; 195662306a36Sopenharmony_ci} 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_cistatic void mpic_resume_one(struct mpic *mpic) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci int i; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci for (i = 0; i < mpic->num_sources; i++) { 196362306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), 196462306a36Sopenharmony_ci mpic->save_data[i].vecprio); 196562306a36Sopenharmony_ci mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 196662306a36Sopenharmony_ci mpic->save_data[i].dest); 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci#ifdef CONFIG_MPIC_U3_HT_IRQS 196962306a36Sopenharmony_ci if (mpic->fixups) { 197062306a36Sopenharmony_ci struct mpic_irq_fixup *fixup = &mpic->fixups[i]; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (fixup->base) { 197362306a36Sopenharmony_ci /* we use the lowest bit in an inverted meaning */ 197462306a36Sopenharmony_ci if ((mpic->save_data[i].fixup_data & 1) == 0) 197562306a36Sopenharmony_ci continue; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* Enable and configure */ 197862306a36Sopenharmony_ci writeb(0x10 + 2 * fixup->index, fixup->base + 2); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci writel(mpic->save_data[i].fixup_data & ~1, 198162306a36Sopenharmony_ci fixup->base + 4); 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci } 198462306a36Sopenharmony_ci#endif 198562306a36Sopenharmony_ci } /* end for loop */ 198662306a36Sopenharmony_ci} 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistatic void mpic_resume(void) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci struct mpic *mpic = mpics; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci while (mpic) { 199362306a36Sopenharmony_ci mpic_resume_one(mpic); 199462306a36Sopenharmony_ci mpic = mpic->next; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci} 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_cistatic struct syscore_ops mpic_syscore_ops = { 199962306a36Sopenharmony_ci .resume = mpic_resume, 200062306a36Sopenharmony_ci .suspend = mpic_suspend, 200162306a36Sopenharmony_ci}; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic int mpic_init_sys(void) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci int rc; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci register_syscore_ops(&mpic_syscore_ops); 200862306a36Sopenharmony_ci rc = subsys_system_register(&mpic_subsys, NULL); 200962306a36Sopenharmony_ci if (rc) { 201062306a36Sopenharmony_ci unregister_syscore_ops(&mpic_syscore_ops); 201162306a36Sopenharmony_ci pr_err("mpic: Failed to register subsystem!\n"); 201262306a36Sopenharmony_ci return rc; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return 0; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cidevice_initcall(mpic_init_sys); 201962306a36Sopenharmony_ci#endif 2020