cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

irq.c (14422B)


      1/*
      2 * This file is subject to the terms and conditions of the GNU General Public
      3 * License.  See the file "COPYING" in the main directory of this archive
      4 * for more details.
      5 *
      6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
      7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/init.h>
     12#include <linux/interrupt.h>
     13#include <linux/irq.h>
     14#include <linux/spinlock.h>
     15#include <asm/irq_cpu.h>
     16#include <asm/mipsregs.h>
     17#include <bcm63xx_cpu.h>
     18#include <bcm63xx_regs.h>
     19#include <bcm63xx_io.h>
     20#include <bcm63xx_irq.h>
     21
     22
     23static DEFINE_SPINLOCK(ipic_lock);
     24static DEFINE_SPINLOCK(epic_lock);
     25
     26static u32 irq_stat_addr[2];
     27static u32 irq_mask_addr[2];
     28static void (*dispatch_internal)(int cpu);
     29static int is_ext_irq_cascaded;
     30static unsigned int ext_irq_count;
     31static unsigned int ext_irq_start, ext_irq_end;
     32static unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
     33static void (*internal_irq_mask)(struct irq_data *d);
     34static void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m);
     35
     36
     37static inline u32 get_ext_irq_perf_reg(int irq)
     38{
     39	if (irq < 4)
     40		return ext_irq_cfg_reg1;
     41	return ext_irq_cfg_reg2;
     42}
     43
     44static inline void handle_internal(int intbit)
     45{
     46	if (is_ext_irq_cascaded &&
     47	    intbit >= ext_irq_start && intbit <= ext_irq_end)
     48		do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
     49	else
     50		do_IRQ(intbit + IRQ_INTERNAL_BASE);
     51}
     52
     53static inline int enable_irq_for_cpu(int cpu, struct irq_data *d,
     54				     const struct cpumask *m)
     55{
     56	bool enable = cpu_online(cpu);
     57
     58#ifdef CONFIG_SMP
     59	if (m)
     60		enable &= cpumask_test_cpu(cpu, m);
     61	else if (irqd_affinity_was_set(d))
     62		enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d));
     63#endif
     64	return enable;
     65}
     66
     67/*
     68 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
     69 * prioritize any interrupt relatively to another. the static counter
     70 * will resume the loop where it ended the last time we left this
     71 * function.
     72 */
     73
     74#define BUILD_IPIC_INTERNAL(width)					\
     75void __dispatch_internal_##width(int cpu)				\
     76{									\
     77	u32 pending[width / 32];					\
     78	unsigned int src, tgt;						\
     79	bool irqs_pending = false;					\
     80	static unsigned int i[2];					\
     81	unsigned int *next = &i[cpu];					\
     82	unsigned long flags;						\
     83									\
     84	/* read registers in reverse order */				\
     85	spin_lock_irqsave(&ipic_lock, flags);				\
     86	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
     87		u32 val;						\
     88									\
     89		val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \
     90		val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \
     91		pending[--tgt] = val;					\
     92									\
     93		if (val)						\
     94			irqs_pending = true;				\
     95	}								\
     96	spin_unlock_irqrestore(&ipic_lock, flags);			\
     97									\
     98	if (!irqs_pending)						\
     99		return;							\
    100									\
    101	while (1) {							\
    102		unsigned int to_call = *next;				\
    103									\
    104		*next = (*next + 1) & (width - 1);			\
    105		if (pending[to_call / 32] & (1 << (to_call & 0x1f))) {	\
    106			handle_internal(to_call);			\
    107			break;						\
    108		}							\
    109	}								\
    110}									\
    111									\
    112static void __internal_irq_mask_##width(struct irq_data *d)		\
    113{									\
    114	u32 val;							\
    115	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
    116	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
    117	unsigned bit = irq & 0x1f;					\
    118	unsigned long flags;						\
    119	int cpu;							\
    120									\
    121	spin_lock_irqsave(&ipic_lock, flags);				\
    122	for_each_present_cpu(cpu) {					\
    123		if (!irq_mask_addr[cpu])				\
    124			break;						\
    125									\
    126		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
    127		val &= ~(1 << bit);					\
    128		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
    129	}								\
    130	spin_unlock_irqrestore(&ipic_lock, flags);			\
    131}									\
    132									\
    133static void __internal_irq_unmask_##width(struct irq_data *d,		\
    134					  const struct cpumask *m)	\
    135{									\
    136	u32 val;							\
    137	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
    138	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
    139	unsigned bit = irq & 0x1f;					\
    140	unsigned long flags;						\
    141	int cpu;							\
    142									\
    143	spin_lock_irqsave(&ipic_lock, flags);				\
    144	for_each_present_cpu(cpu) {					\
    145		if (!irq_mask_addr[cpu])				\
    146			break;						\
    147									\
    148		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
    149		if (enable_irq_for_cpu(cpu, d, m))			\
    150			val |= (1 << bit);				\
    151		else							\
    152			val &= ~(1 << bit);				\
    153		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
    154	}								\
    155	spin_unlock_irqrestore(&ipic_lock, flags);			\
    156}
    157
    158BUILD_IPIC_INTERNAL(32);
    159BUILD_IPIC_INTERNAL(64);
    160
    161asmlinkage void plat_irq_dispatch(void)
    162{
    163	u32 cause;
    164
    165	do {
    166		cause = read_c0_cause() & read_c0_status() & ST0_IM;
    167
    168		if (!cause)
    169			break;
    170
    171		if (cause & CAUSEF_IP7)
    172			do_IRQ(7);
    173		if (cause & CAUSEF_IP0)
    174			do_IRQ(0);
    175		if (cause & CAUSEF_IP1)
    176			do_IRQ(1);
    177		if (cause & CAUSEF_IP2)
    178			dispatch_internal(0);
    179		if (is_ext_irq_cascaded) {
    180			if (cause & CAUSEF_IP3)
    181				dispatch_internal(1);
    182		} else {
    183			if (cause & CAUSEF_IP3)
    184				do_IRQ(IRQ_EXT_0);
    185			if (cause & CAUSEF_IP4)
    186				do_IRQ(IRQ_EXT_1);
    187			if (cause & CAUSEF_IP5)
    188				do_IRQ(IRQ_EXT_2);
    189			if (cause & CAUSEF_IP6)
    190				do_IRQ(IRQ_EXT_3);
    191		}
    192	} while (1);
    193}
    194
    195/*
    196 * internal IRQs operations: only mask/unmask on PERF irq mask
    197 * register.
    198 */
    199static void bcm63xx_internal_irq_mask(struct irq_data *d)
    200{
    201	internal_irq_mask(d);
    202}
    203
    204static void bcm63xx_internal_irq_unmask(struct irq_data *d)
    205{
    206	internal_irq_unmask(d, NULL);
    207}
    208
    209/*
    210 * external IRQs operations: mask/unmask and clear on PERF external
    211 * irq control register.
    212 */
    213static void bcm63xx_external_irq_mask(struct irq_data *d)
    214{
    215	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
    216	u32 reg, regaddr;
    217	unsigned long flags;
    218
    219	regaddr = get_ext_irq_perf_reg(irq);
    220	spin_lock_irqsave(&epic_lock, flags);
    221	reg = bcm_perf_readl(regaddr);
    222
    223	if (BCMCPU_IS_6348())
    224		reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
    225	else
    226		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
    227
    228	bcm_perf_writel(reg, regaddr);
    229	spin_unlock_irqrestore(&epic_lock, flags);
    230
    231	if (is_ext_irq_cascaded)
    232		internal_irq_mask(irq_get_irq_data(irq + ext_irq_start));
    233}
    234
    235static void bcm63xx_external_irq_unmask(struct irq_data *d)
    236{
    237	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
    238	u32 reg, regaddr;
    239	unsigned long flags;
    240
    241	regaddr = get_ext_irq_perf_reg(irq);
    242	spin_lock_irqsave(&epic_lock, flags);
    243	reg = bcm_perf_readl(regaddr);
    244
    245	if (BCMCPU_IS_6348())
    246		reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
    247	else
    248		reg |= EXTIRQ_CFG_MASK(irq % 4);
    249
    250	bcm_perf_writel(reg, regaddr);
    251	spin_unlock_irqrestore(&epic_lock, flags);
    252
    253	if (is_ext_irq_cascaded)
    254		internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start),
    255				    NULL);
    256}
    257
    258static void bcm63xx_external_irq_clear(struct irq_data *d)
    259{
    260	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
    261	u32 reg, regaddr;
    262	unsigned long flags;
    263
    264	regaddr = get_ext_irq_perf_reg(irq);
    265	spin_lock_irqsave(&epic_lock, flags);
    266	reg = bcm_perf_readl(regaddr);
    267
    268	if (BCMCPU_IS_6348())
    269		reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
    270	else
    271		reg |= EXTIRQ_CFG_CLEAR(irq % 4);
    272
    273	bcm_perf_writel(reg, regaddr);
    274	spin_unlock_irqrestore(&epic_lock, flags);
    275}
    276
    277static int bcm63xx_external_irq_set_type(struct irq_data *d,
    278					 unsigned int flow_type)
    279{
    280	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
    281	u32 reg, regaddr;
    282	int levelsense, sense, bothedge;
    283	unsigned long flags;
    284
    285	flow_type &= IRQ_TYPE_SENSE_MASK;
    286
    287	if (flow_type == IRQ_TYPE_NONE)
    288		flow_type = IRQ_TYPE_LEVEL_LOW;
    289
    290	levelsense = sense = bothedge = 0;
    291	switch (flow_type) {
    292	case IRQ_TYPE_EDGE_BOTH:
    293		bothedge = 1;
    294		break;
    295
    296	case IRQ_TYPE_EDGE_RISING:
    297		sense = 1;
    298		break;
    299
    300	case IRQ_TYPE_EDGE_FALLING:
    301		break;
    302
    303	case IRQ_TYPE_LEVEL_HIGH:
    304		levelsense = 1;
    305		sense = 1;
    306		break;
    307
    308	case IRQ_TYPE_LEVEL_LOW:
    309		levelsense = 1;
    310		break;
    311
    312	default:
    313		pr_err("bogus flow type combination given !\n");
    314		return -EINVAL;
    315	}
    316
    317	regaddr = get_ext_irq_perf_reg(irq);
    318	spin_lock_irqsave(&epic_lock, flags);
    319	reg = bcm_perf_readl(regaddr);
    320	irq %= 4;
    321
    322	switch (bcm63xx_get_cpu_id()) {
    323	case BCM6348_CPU_ID:
    324		if (levelsense)
    325			reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
    326		else
    327			reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
    328		if (sense)
    329			reg |= EXTIRQ_CFG_SENSE_6348(irq);
    330		else
    331			reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
    332		if (bothedge)
    333			reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
    334		else
    335			reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
    336		break;
    337
    338	case BCM3368_CPU_ID:
    339	case BCM6328_CPU_ID:
    340	case BCM6338_CPU_ID:
    341	case BCM6345_CPU_ID:
    342	case BCM6358_CPU_ID:
    343	case BCM6362_CPU_ID:
    344	case BCM6368_CPU_ID:
    345		if (levelsense)
    346			reg |= EXTIRQ_CFG_LEVELSENSE(irq);
    347		else
    348			reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
    349		if (sense)
    350			reg |= EXTIRQ_CFG_SENSE(irq);
    351		else
    352			reg &= ~EXTIRQ_CFG_SENSE(irq);
    353		if (bothedge)
    354			reg |= EXTIRQ_CFG_BOTHEDGE(irq);
    355		else
    356			reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
    357		break;
    358	default:
    359		BUG();
    360	}
    361
    362	bcm_perf_writel(reg, regaddr);
    363	spin_unlock_irqrestore(&epic_lock, flags);
    364
    365	irqd_set_trigger_type(d, flow_type);
    366	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
    367		irq_set_handler_locked(d, handle_level_irq);
    368	else
    369		irq_set_handler_locked(d, handle_edge_irq);
    370
    371	return IRQ_SET_MASK_OK_NOCOPY;
    372}
    373
    374#ifdef CONFIG_SMP
    375static int bcm63xx_internal_set_affinity(struct irq_data *data,
    376					 const struct cpumask *dest,
    377					 bool force)
    378{
    379	if (!irqd_irq_disabled(data))
    380		internal_irq_unmask(data, dest);
    381
    382	return 0;
    383}
    384#endif
    385
    386static struct irq_chip bcm63xx_internal_irq_chip = {
    387	.name		= "bcm63xx_ipic",
    388	.irq_mask	= bcm63xx_internal_irq_mask,
    389	.irq_unmask	= bcm63xx_internal_irq_unmask,
    390};
    391
    392static struct irq_chip bcm63xx_external_irq_chip = {
    393	.name		= "bcm63xx_epic",
    394	.irq_ack	= bcm63xx_external_irq_clear,
    395
    396	.irq_mask	= bcm63xx_external_irq_mask,
    397	.irq_unmask	= bcm63xx_external_irq_unmask,
    398
    399	.irq_set_type	= bcm63xx_external_irq_set_type,
    400};
    401
    402static void bcm63xx_init_irq(void)
    403{
    404	int irq_bits;
    405
    406	irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF);
    407	irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF);
    408	irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF);
    409	irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF);
    410
    411	switch (bcm63xx_get_cpu_id()) {
    412	case BCM3368_CPU_ID:
    413		irq_stat_addr[0] += PERF_IRQSTAT_3368_REG;
    414		irq_mask_addr[0] += PERF_IRQMASK_3368_REG;
    415		irq_stat_addr[1] = 0;
    416		irq_mask_addr[1] = 0;
    417		irq_bits = 32;
    418		ext_irq_count = 4;
    419		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
    420		break;
    421	case BCM6328_CPU_ID:
    422		irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0);
    423		irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0);
    424		irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1);
    425		irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1);
    426		irq_bits = 64;
    427		ext_irq_count = 4;
    428		is_ext_irq_cascaded = 1;
    429		ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
    430		ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
    431		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
    432		break;
    433	case BCM6338_CPU_ID:
    434		irq_stat_addr[0] += PERF_IRQSTAT_6338_REG;
    435		irq_mask_addr[0] += PERF_IRQMASK_6338_REG;
    436		irq_stat_addr[1] = 0;
    437		irq_mask_addr[1] = 0;
    438		irq_bits = 32;
    439		ext_irq_count = 4;
    440		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
    441		break;
    442	case BCM6345_CPU_ID:
    443		irq_stat_addr[0] += PERF_IRQSTAT_6345_REG;
    444		irq_mask_addr[0] += PERF_IRQMASK_6345_REG;
    445		irq_stat_addr[1] = 0;
    446		irq_mask_addr[1] = 0;
    447		irq_bits = 32;
    448		ext_irq_count = 4;
    449		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
    450		break;
    451	case BCM6348_CPU_ID:
    452		irq_stat_addr[0] += PERF_IRQSTAT_6348_REG;
    453		irq_mask_addr[0] += PERF_IRQMASK_6348_REG;
    454		irq_stat_addr[1] = 0;
    455		irq_mask_addr[1] = 0;
    456		irq_bits = 32;
    457		ext_irq_count = 4;
    458		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
    459		break;
    460	case BCM6358_CPU_ID:
    461		irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0);
    462		irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0);
    463		irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1);
    464		irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1);
    465		irq_bits = 32;
    466		ext_irq_count = 4;
    467		is_ext_irq_cascaded = 1;
    468		ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
    469		ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
    470		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
    471		break;
    472	case BCM6362_CPU_ID:
    473		irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0);
    474		irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0);
    475		irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1);
    476		irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1);
    477		irq_bits = 64;
    478		ext_irq_count = 4;
    479		is_ext_irq_cascaded = 1;
    480		ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
    481		ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
    482		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
    483		break;
    484	case BCM6368_CPU_ID:
    485		irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0);
    486		irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0);
    487		irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1);
    488		irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1);
    489		irq_bits = 64;
    490		ext_irq_count = 6;
    491		is_ext_irq_cascaded = 1;
    492		ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
    493		ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
    494		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
    495		ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
    496		break;
    497	default:
    498		BUG();
    499	}
    500
    501	if (irq_bits == 32) {
    502		dispatch_internal = __dispatch_internal_32;
    503		internal_irq_mask = __internal_irq_mask_32;
    504		internal_irq_unmask = __internal_irq_unmask_32;
    505	} else {
    506		dispatch_internal = __dispatch_internal_64;
    507		internal_irq_mask = __internal_irq_mask_64;
    508		internal_irq_unmask = __internal_irq_unmask_64;
    509	}
    510}
    511
    512void __init arch_init_irq(void)
    513{
    514	int i, irq;
    515
    516	bcm63xx_init_irq();
    517	mips_cpu_irq_init();
    518	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
    519		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
    520					 handle_level_irq);
    521
    522	for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
    523		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
    524					 handle_edge_irq);
    525
    526	if (!is_ext_irq_cascaded) {
    527		for (i = 3; i < 3 + ext_irq_count; ++i) {
    528			irq = MIPS_CPU_IRQ_BASE + i;
    529			if (request_irq(irq, no_action, IRQF_NO_THREAD,
    530					"cascade_extirq", NULL)) {
    531				pr_err("Failed to request irq %d (cascade_extirq)\n",
    532				       irq);
    533			}
    534		}
    535	}
    536
    537	irq = MIPS_CPU_IRQ_BASE + 2;
    538	if (request_irq(irq, no_action, IRQF_NO_THREAD,	"cascade_ip2", NULL))
    539		pr_err("Failed to request irq %d (cascade_ip2)\n", irq);
    540#ifdef CONFIG_SMP
    541	if (is_ext_irq_cascaded) {
    542		irq = MIPS_CPU_IRQ_BASE + 3;
    543		if (request_irq(irq, no_action,	IRQF_NO_THREAD, "cascade_ip3",
    544				NULL))
    545			pr_err("Failed to request irq %d (cascade_ip3)\n", irq);
    546		bcm63xx_internal_irq_chip.irq_set_affinity =
    547			bcm63xx_internal_set_affinity;
    548
    549		cpumask_clear(irq_default_affinity);
    550		cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
    551	}
    552#endif
    553}