18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cell Broadband Engine Performance Monitor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright IBM Corporation 2001,2006
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author:
88c2ecf20Sopenharmony_ci *    David Erb (djerb@us.ibm.com)
98c2ecf20Sopenharmony_ci *    Kevin Corry (kevcorry@us.ibm.com)
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci#include <asm/io.h>
168c2ecf20Sopenharmony_ci#include <asm/irq_regs.h>
178c2ecf20Sopenharmony_ci#include <asm/machdep.h>
188c2ecf20Sopenharmony_ci#include <asm/pmc.h>
198c2ecf20Sopenharmony_ci#include <asm/reg.h>
208c2ecf20Sopenharmony_ci#include <asm/spu.h>
218c2ecf20Sopenharmony_ci#include <asm/cell-regs.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "interrupt.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * When writing to write-only mmio addresses, save a shadow copy. All of the
278c2ecf20Sopenharmony_ci * registers are 32-bit, but stored in the upper-half of a 64-bit field in
288c2ecf20Sopenharmony_ci * pmd_regs.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define WRITE_WO_MMIO(reg, x)					\
328c2ecf20Sopenharmony_ci	do {							\
338c2ecf20Sopenharmony_ci		u32 _x = (x);					\
348c2ecf20Sopenharmony_ci		struct cbe_pmd_regs __iomem *pmd_regs;		\
358c2ecf20Sopenharmony_ci		struct cbe_pmd_shadow_regs *shadow_regs;	\
368c2ecf20Sopenharmony_ci		pmd_regs = cbe_get_cpu_pmd_regs(cpu);		\
378c2ecf20Sopenharmony_ci		shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);	\
388c2ecf20Sopenharmony_ci		out_be64(&(pmd_regs->reg), (((u64)_x) << 32));	\
398c2ecf20Sopenharmony_ci		shadow_regs->reg = _x;				\
408c2ecf20Sopenharmony_ci	} while (0)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define READ_SHADOW_REG(val, reg)				\
438c2ecf20Sopenharmony_ci	do {							\
448c2ecf20Sopenharmony_ci		struct cbe_pmd_shadow_regs *shadow_regs;	\
458c2ecf20Sopenharmony_ci		shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);	\
468c2ecf20Sopenharmony_ci		(val) = shadow_regs->reg;			\
478c2ecf20Sopenharmony_ci	} while (0)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define READ_MMIO_UPPER32(val, reg)				\
508c2ecf20Sopenharmony_ci	do {							\
518c2ecf20Sopenharmony_ci		struct cbe_pmd_regs __iomem *pmd_regs;		\
528c2ecf20Sopenharmony_ci		pmd_regs = cbe_get_cpu_pmd_regs(cpu);		\
538c2ecf20Sopenharmony_ci		(val) = (u32)(in_be64(&pmd_regs->reg) >> 32);	\
548c2ecf20Sopenharmony_ci	} while (0)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Physical counter registers.
588c2ecf20Sopenharmony_ci * Each physical counter can act as one 32-bit counter or two 16-bit counters.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciu32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u32 val_in_latch, val = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (phys_ctr < NR_PHYS_CTRS) {
668c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val_in_latch, counter_value_in_latch);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		/* Read the latch or the actual counter, whichever is newer. */
698c2ecf20Sopenharmony_ci		if (val_in_latch & (1 << phys_ctr)) {
708c2ecf20Sopenharmony_ci			READ_SHADOW_REG(val, pm_ctr[phys_ctr]);
718c2ecf20Sopenharmony_ci		} else {
728c2ecf20Sopenharmony_ci			READ_MMIO_UPPER32(val, pm_ctr[phys_ctr]);
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return val;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_read_phys_ctr);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct cbe_pmd_shadow_regs *shadow_regs;
838c2ecf20Sopenharmony_ci	u32 pm_ctrl;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (phys_ctr < NR_PHYS_CTRS) {
868c2ecf20Sopenharmony_ci		/* Writing to a counter only writes to a hardware latch.
878c2ecf20Sopenharmony_ci		 * The new value is not propagated to the actual counter
888c2ecf20Sopenharmony_ci		 * until the performance monitor is enabled.
898c2ecf20Sopenharmony_ci		 */
908c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm_ctr[phys_ctr], val);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		pm_ctrl = cbe_read_pm(cpu, pm_control);
938c2ecf20Sopenharmony_ci		if (pm_ctrl & CBE_PM_ENABLE_PERF_MON) {
948c2ecf20Sopenharmony_ci			/* The counters are already active, so we need to
958c2ecf20Sopenharmony_ci			 * rewrite the pm_control register to "re-enable"
968c2ecf20Sopenharmony_ci			 * the PMU.
978c2ecf20Sopenharmony_ci			 */
988c2ecf20Sopenharmony_ci			cbe_write_pm(cpu, pm_control, pm_ctrl);
998c2ecf20Sopenharmony_ci		} else {
1008c2ecf20Sopenharmony_ci			shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);
1018c2ecf20Sopenharmony_ci			shadow_regs->counter_value_in_latch |= (1 << phys_ctr);
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_write_phys_ctr);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/*
1088c2ecf20Sopenharmony_ci * "Logical" counter registers.
1098c2ecf20Sopenharmony_ci * These will read/write 16-bits or 32-bits depending on the
1108c2ecf20Sopenharmony_ci * current size of the counter. Counters 4 - 7 are always 16-bit.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciu32 cbe_read_ctr(u32 cpu, u32 ctr)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u32 val;
1168c2ecf20Sopenharmony_ci	u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	val = cbe_read_phys_ctr(cpu, phys_ctr);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (cbe_get_ctr_size(cpu, phys_ctr) == 16)
1218c2ecf20Sopenharmony_ci		val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return val;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_read_ctr);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid cbe_write_ctr(u32 cpu, u32 ctr, u32 val)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	u32 phys_ctr;
1308c2ecf20Sopenharmony_ci	u32 phys_val;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	phys_ctr = ctr & (NR_PHYS_CTRS - 1);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (cbe_get_ctr_size(cpu, phys_ctr) == 16) {
1358c2ecf20Sopenharmony_ci		phys_val = cbe_read_phys_ctr(cpu, phys_ctr);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (ctr < NR_PHYS_CTRS)
1388c2ecf20Sopenharmony_ci			val = (val << 16) | (phys_val & 0xffff);
1398c2ecf20Sopenharmony_ci		else
1408c2ecf20Sopenharmony_ci			val = (val & 0xffff) | (phys_val & 0xffff0000);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	cbe_write_phys_ctr(cpu, phys_ctr, val);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_write_ctr);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/*
1488c2ecf20Sopenharmony_ci * Counter-control registers.
1498c2ecf20Sopenharmony_ci * Each "logical" counter has a corresponding control register.
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciu32 cbe_read_pm07_control(u32 cpu, u32 ctr)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u32 pm07_control = 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (ctr < NR_CTRS)
1578c2ecf20Sopenharmony_ci		READ_SHADOW_REG(pm07_control, pm07_control[ctr]);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return pm07_control;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_read_pm07_control);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_civoid cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	if (ctr < NR_CTRS)
1668c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm07_control[ctr], val);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_write_pm07_control);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/*
1718c2ecf20Sopenharmony_ci * Other PMU control registers. Most of these are write-only.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ciu32 cbe_read_pm(u32 cpu, enum pm_reg_name reg)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	u32 val = 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	switch (reg) {
1798c2ecf20Sopenharmony_ci	case group_control:
1808c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val, group_control);
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	case debug_bus_control:
1848c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val, debug_bus_control);
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	case trace_address:
1888c2ecf20Sopenharmony_ci		READ_MMIO_UPPER32(val, trace_address);
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	case ext_tr_timer:
1928c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val, ext_tr_timer);
1938c2ecf20Sopenharmony_ci		break;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	case pm_status:
1968c2ecf20Sopenharmony_ci		READ_MMIO_UPPER32(val, pm_status);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	case pm_control:
2008c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val, pm_control);
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	case pm_interval:
2048c2ecf20Sopenharmony_ci		READ_MMIO_UPPER32(val, pm_interval);
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	case pm_start_stop:
2088c2ecf20Sopenharmony_ci		READ_SHADOW_REG(val, pm_start_stop);
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return val;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_read_pm);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_civoid cbe_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	switch (reg) {
2198c2ecf20Sopenharmony_ci	case group_control:
2208c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(group_control, val);
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	case debug_bus_control:
2248c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(debug_bus_control, val);
2258c2ecf20Sopenharmony_ci		break;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	case trace_address:
2288c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(trace_address, val);
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	case ext_tr_timer:
2328c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(ext_tr_timer, val);
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	case pm_status:
2368c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm_status, val);
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	case pm_control:
2408c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm_control, val);
2418c2ecf20Sopenharmony_ci		break;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	case pm_interval:
2448c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm_interval, val);
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	case pm_start_stop:
2488c2ecf20Sopenharmony_ci		WRITE_WO_MMIO(pm_start_stop, val);
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_write_pm);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * Get/set the size of a physical counter to either 16 or 32 bits.
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciu32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	u32 pm_ctrl, size = 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (phys_ctr < NR_PHYS_CTRS) {
2638c2ecf20Sopenharmony_ci		pm_ctrl = cbe_read_pm(cpu, pm_control);
2648c2ecf20Sopenharmony_ci		size = (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return size;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_ctr_size);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_civoid cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	u32 pm_ctrl;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (phys_ctr < NR_PHYS_CTRS) {
2768c2ecf20Sopenharmony_ci		pm_ctrl = cbe_read_pm(cpu, pm_control);
2778c2ecf20Sopenharmony_ci		switch (ctr_size) {
2788c2ecf20Sopenharmony_ci		case 16:
2798c2ecf20Sopenharmony_ci			pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr);
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		case 32:
2838c2ecf20Sopenharmony_ci			pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr);
2848c2ecf20Sopenharmony_ci			break;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		cbe_write_pm(cpu, pm_control, pm_ctrl);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_set_ctr_size);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/*
2928c2ecf20Sopenharmony_ci * Enable/disable the entire performance monitoring unit.
2938c2ecf20Sopenharmony_ci * When we enable the PMU, all pending writes to counters get committed.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_civoid cbe_enable_pm(u32 cpu)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct cbe_pmd_shadow_regs *shadow_regs;
2998c2ecf20Sopenharmony_ci	u32 pm_ctrl;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);
3028c2ecf20Sopenharmony_ci	shadow_regs->counter_value_in_latch = 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	pm_ctrl = cbe_read_pm(cpu, pm_control) | CBE_PM_ENABLE_PERF_MON;
3058c2ecf20Sopenharmony_ci	cbe_write_pm(cpu, pm_control, pm_ctrl);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_enable_pm);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_civoid cbe_disable_pm(u32 cpu)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	u32 pm_ctrl;
3128c2ecf20Sopenharmony_ci	pm_ctrl = cbe_read_pm(cpu, pm_control) & ~CBE_PM_ENABLE_PERF_MON;
3138c2ecf20Sopenharmony_ci	cbe_write_pm(cpu, pm_control, pm_ctrl);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_disable_pm);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * Reading from the trace_buffer.
3198c2ecf20Sopenharmony_ci * The trace buffer is two 64-bit registers. Reading from
3208c2ecf20Sopenharmony_ci * the second half automatically increments the trace_address.
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_civoid cbe_read_trace_buffer(u32 cpu, u64 *buf)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	*buf++ = in_be64(&pmd_regs->trace_buffer_0_63);
3288c2ecf20Sopenharmony_ci	*buf++ = in_be64(&pmd_regs->trace_buffer_64_127);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_read_trace_buffer);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/*
3338c2ecf20Sopenharmony_ci * Enabling/disabling interrupts for the entire performance monitoring unit.
3348c2ecf20Sopenharmony_ci */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ciu32 cbe_get_and_clear_pm_interrupts(u32 cpu)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	/* Reading pm_status clears the interrupt bits. */
3398c2ecf20Sopenharmony_ci	return cbe_read_pm(cpu, pm_status);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_and_clear_pm_interrupts);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_civoid cbe_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	/* Set which node and thread will handle the next interrupt. */
3468c2ecf20Sopenharmony_ci	iic_set_interrupt_routing(cpu, thread, 0);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* Enable the interrupt bits in the pm_status register. */
3498c2ecf20Sopenharmony_ci	if (mask)
3508c2ecf20Sopenharmony_ci		cbe_write_pm(cpu, pm_status, mask);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_enable_pm_interrupts);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_civoid cbe_disable_pm_interrupts(u32 cpu)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	cbe_get_and_clear_pm_interrupts(cpu);
3578c2ecf20Sopenharmony_ci	cbe_write_pm(cpu, pm_status, 0);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_disable_pm_interrupts);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic irqreturn_t cbe_pm_irq(int irq, void *dev_id)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	perf_irq(get_irq_regs());
3648c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int __init cbe_init_pm_irq(void)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	unsigned int irq;
3708c2ecf20Sopenharmony_ci	int rc, node;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	for_each_online_node(node) {
3738c2ecf20Sopenharmony_ci		irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI |
3748c2ecf20Sopenharmony_ci					       (node << IIC_IRQ_NODE_SHIFT));
3758c2ecf20Sopenharmony_ci		if (!irq) {
3768c2ecf20Sopenharmony_ci			printk("ERROR: Unable to allocate irq for node %d\n",
3778c2ecf20Sopenharmony_ci			       node);
3788c2ecf20Sopenharmony_ci			return -EINVAL;
3798c2ecf20Sopenharmony_ci		}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		rc = request_irq(irq, cbe_pm_irq,
3828c2ecf20Sopenharmony_ci				 0, "cbe-pmu-0", NULL);
3838c2ecf20Sopenharmony_ci		if (rc) {
3848c2ecf20Sopenharmony_ci			printk("ERROR: Request for irq on node %d failed\n",
3858c2ecf20Sopenharmony_ci			       node);
3868c2ecf20Sopenharmony_ci			return rc;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_cimachine_arch_initcall(cell, cbe_init_pm_irq);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_civoid cbe_sync_irq(int node)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	unsigned int irq;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	irq = irq_find_mapping(NULL,
3998c2ecf20Sopenharmony_ci			       IIC_IRQ_IOEX_PMI
4008c2ecf20Sopenharmony_ci			       | (node << IIC_IRQ_NODE_SHIFT));
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (!irq) {
4038c2ecf20Sopenharmony_ci		printk(KERN_WARNING "ERROR, unable to get existing irq %d " \
4048c2ecf20Sopenharmony_ci		"for node %d\n", irq, node);
4058c2ecf20Sopenharmony_ci		return;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	synchronize_irq(irq);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_sync_irq);
4118c2ecf20Sopenharmony_ci
412