18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * @file op_model_amd.c
38c2ecf20Sopenharmony_ci * athlon / K7 / K8 / Family 10h model-specific MSR operations
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * @remark Copyright 2002-2009 OProfile authors
68c2ecf20Sopenharmony_ci * @remark Read the file COPYING
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * @author John Levon
98c2ecf20Sopenharmony_ci * @author Philippe Elie
108c2ecf20Sopenharmony_ci * @author Graydon Hoare
118c2ecf20Sopenharmony_ci * @author Robert Richter <robert.richter@amd.com>
128c2ecf20Sopenharmony_ci * @author Barry Kasindorf <barry.kasindorf@amd.com>
138c2ecf20Sopenharmony_ci * @author Jason Yeh <jason.yeh@amd.com>
148c2ecf20Sopenharmony_ci * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
188c2ecf20Sopenharmony_ci#include <linux/device.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/percpu.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
238c2ecf20Sopenharmony_ci#include <asm/msr.h>
248c2ecf20Sopenharmony_ci#include <asm/nmi.h>
258c2ecf20Sopenharmony_ci#include <asm/apic.h>
268c2ecf20Sopenharmony_ci#include <asm/processor.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "op_x86_model.h"
298c2ecf20Sopenharmony_ci#include "op_counter.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
328c2ecf20Sopenharmony_ci#define NUM_VIRT_COUNTERS	32
338c2ecf20Sopenharmony_ci#else
348c2ecf20Sopenharmony_ci#define NUM_VIRT_COUNTERS	0
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define OP_EVENT_MASK			0x0FFF
388c2ecf20Sopenharmony_ci#define OP_CTR_OVERFLOW			(1ULL<<31)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MSR_AMD_EVENTSEL_RESERVED	((0xFFFFFCF0ULL<<32)|(1ULL<<21))
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int num_counters;
438c2ecf20Sopenharmony_cistatic unsigned long reset_value[OP_MAX_COUNTER];
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define IBS_FETCH_SIZE			6
468c2ecf20Sopenharmony_ci#define IBS_OP_SIZE			12
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic u32 ibs_caps;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct ibs_config {
518c2ecf20Sopenharmony_ci	unsigned long op_enabled;
528c2ecf20Sopenharmony_ci	unsigned long fetch_enabled;
538c2ecf20Sopenharmony_ci	unsigned long max_cnt_fetch;
548c2ecf20Sopenharmony_ci	unsigned long max_cnt_op;
558c2ecf20Sopenharmony_ci	unsigned long rand_en;
568c2ecf20Sopenharmony_ci	unsigned long dispatched_ops;
578c2ecf20Sopenharmony_ci	unsigned long branch_target;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct ibs_state {
618c2ecf20Sopenharmony_ci	u64		ibs_op_ctl;
628c2ecf20Sopenharmony_ci	int		branch_target;
638c2ecf20Sopenharmony_ci	unsigned long	sample_size;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct ibs_config ibs_config;
678c2ecf20Sopenharmony_cistatic struct ibs_state ibs_state;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * IBS randomization macros
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_ci#define IBS_RANDOM_BITS			12
738c2ecf20Sopenharmony_ci#define IBS_RANDOM_MASK			((1ULL << IBS_RANDOM_BITS) - 1)
748c2ecf20Sopenharmony_ci#define IBS_RANDOM_MAXCNT_OFFSET	(1ULL << (IBS_RANDOM_BITS - 5))
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * 16-bit Linear Feedback Shift Register (LFSR)
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci *                       16   14   13    11
808c2ecf20Sopenharmony_ci * Feedback polynomial = X  + X  + X  +  X  + 1
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic unsigned int lfsr_random(void)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	static unsigned int lfsr_value = 0xF00D;
858c2ecf20Sopenharmony_ci	unsigned int bit;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Compute next bit to shift in */
888c2ecf20Sopenharmony_ci	bit = ((lfsr_value >> 0) ^
898c2ecf20Sopenharmony_ci	       (lfsr_value >> 2) ^
908c2ecf20Sopenharmony_ci	       (lfsr_value >> 3) ^
918c2ecf20Sopenharmony_ci	       (lfsr_value >> 5)) & 0x0001;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Advance to next register value */
948c2ecf20Sopenharmony_ci	lfsr_value = (lfsr_value >> 1) | (bit << 15);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return lfsr_value;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * IBS software randomization
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * The IBS periodic op counter is randomized in software. The lower 12
1038c2ecf20Sopenharmony_ci * bits of the 20 bit counter are randomized. IbsOpCurCnt is
1048c2ecf20Sopenharmony_ci * initialized with a 12 bit random value.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic inline u64 op_amd_randomize_ibs_op(u64 val)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	unsigned int random = lfsr_random();
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!(ibs_caps & IBS_CAPS_RDWROPCNT))
1118c2ecf20Sopenharmony_ci		/*
1128c2ecf20Sopenharmony_ci		 * Work around if the hw can not write to IbsOpCurCnt
1138c2ecf20Sopenharmony_ci		 *
1148c2ecf20Sopenharmony_ci		 * Randomize the lower 8 bits of the 16 bit
1158c2ecf20Sopenharmony_ci		 * IbsOpMaxCnt [15:0] value in the range of -128 to
1168c2ecf20Sopenharmony_ci		 * +127 by adding/subtracting an offset to the
1178c2ecf20Sopenharmony_ci		 * maximum count (IbsOpMaxCnt).
1188c2ecf20Sopenharmony_ci		 *
1198c2ecf20Sopenharmony_ci		 * To avoid over or underflows and protect upper bits
1208c2ecf20Sopenharmony_ci		 * starting at bit 16, the initial value for
1218c2ecf20Sopenharmony_ci		 * IbsOpMaxCnt must fit in the range from 0x0081 to
1228c2ecf20Sopenharmony_ci		 * 0xff80.
1238c2ecf20Sopenharmony_ci		 */
1248c2ecf20Sopenharmony_ci		val += (s8)(random >> 4);
1258c2ecf20Sopenharmony_ci	else
1268c2ecf20Sopenharmony_ci		val |= (u64)(random & IBS_RANDOM_MASK) << 32;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return val;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline void
1328c2ecf20Sopenharmony_ciop_amd_handle_ibs(struct pt_regs * const regs,
1338c2ecf20Sopenharmony_ci		  struct op_msrs const * const msrs)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	u64 val, ctl;
1368c2ecf20Sopenharmony_ci	struct op_entry entry;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!ibs_caps)
1398c2ecf20Sopenharmony_ci		return;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (ibs_config.fetch_enabled) {
1428c2ecf20Sopenharmony_ci		rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl);
1438c2ecf20Sopenharmony_ci		if (ctl & IBS_FETCH_VAL) {
1448c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSFETCHLINAD, val);
1458c2ecf20Sopenharmony_ci			oprofile_write_reserve(&entry, regs, val,
1468c2ecf20Sopenharmony_ci					       IBS_FETCH_CODE, IBS_FETCH_SIZE);
1478c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1488c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, ctl);
1498c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val);
1508c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1518c2ecf20Sopenharmony_ci			oprofile_write_commit(&entry);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci			/* reenable the IRQ */
1548c2ecf20Sopenharmony_ci			ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT);
1558c2ecf20Sopenharmony_ci			ctl |= IBS_FETCH_ENABLE;
1568c2ecf20Sopenharmony_ci			wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl);
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (ibs_config.op_enabled) {
1618c2ecf20Sopenharmony_ci		rdmsrl(MSR_AMD64_IBSOPCTL, ctl);
1628c2ecf20Sopenharmony_ci		if (ctl & IBS_OP_VAL) {
1638c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSOPRIP, val);
1648c2ecf20Sopenharmony_ci			oprofile_write_reserve(&entry, regs, val, IBS_OP_CODE,
1658c2ecf20Sopenharmony_ci					       ibs_state.sample_size);
1668c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1678c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSOPDATA, val);
1688c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1698c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSOPDATA2, val);
1708c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1718c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSOPDATA3, val);
1728c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1738c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSDCLINAD, val);
1748c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1758c2ecf20Sopenharmony_ci			rdmsrl(MSR_AMD64_IBSDCPHYSAD, val);
1768c2ecf20Sopenharmony_ci			oprofile_add_data64(&entry, val);
1778c2ecf20Sopenharmony_ci			if (ibs_state.branch_target) {
1788c2ecf20Sopenharmony_ci				rdmsrl(MSR_AMD64_IBSBRTARGET, val);
1798c2ecf20Sopenharmony_ci				oprofile_add_data(&entry, (unsigned long)val);
1808c2ecf20Sopenharmony_ci			}
1818c2ecf20Sopenharmony_ci			oprofile_write_commit(&entry);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci			/* reenable the IRQ */
1848c2ecf20Sopenharmony_ci			ctl = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
1858c2ecf20Sopenharmony_ci			wrmsrl(MSR_AMD64_IBSOPCTL, ctl);
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic inline void op_amd_start_ibs(void)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	u64 val;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!ibs_caps)
1958c2ecf20Sopenharmony_ci		return;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	memset(&ibs_state, 0, sizeof(ibs_state));
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/*
2008c2ecf20Sopenharmony_ci	 * Note: Since the max count settings may out of range we
2018c2ecf20Sopenharmony_ci	 * write back the actual used values so that userland can read
2028c2ecf20Sopenharmony_ci	 * it.
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (ibs_config.fetch_enabled) {
2068c2ecf20Sopenharmony_ci		val = ibs_config.max_cnt_fetch >> 4;
2078c2ecf20Sopenharmony_ci		val = min(val, IBS_FETCH_MAX_CNT);
2088c2ecf20Sopenharmony_ci		ibs_config.max_cnt_fetch = val << 4;
2098c2ecf20Sopenharmony_ci		val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0;
2108c2ecf20Sopenharmony_ci		val |= IBS_FETCH_ENABLE;
2118c2ecf20Sopenharmony_ci		wrmsrl(MSR_AMD64_IBSFETCHCTL, val);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (ibs_config.op_enabled) {
2158c2ecf20Sopenharmony_ci		val = ibs_config.max_cnt_op >> 4;
2168c2ecf20Sopenharmony_ci		if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) {
2178c2ecf20Sopenharmony_ci			/*
2188c2ecf20Sopenharmony_ci			 * IbsOpCurCnt not supported.  See
2198c2ecf20Sopenharmony_ci			 * op_amd_randomize_ibs_op() for details.
2208c2ecf20Sopenharmony_ci			 */
2218c2ecf20Sopenharmony_ci			val = clamp(val, 0x0081ULL, 0xFF80ULL);
2228c2ecf20Sopenharmony_ci			ibs_config.max_cnt_op = val << 4;
2238c2ecf20Sopenharmony_ci		} else {
2248c2ecf20Sopenharmony_ci			/*
2258c2ecf20Sopenharmony_ci			 * The start value is randomized with a
2268c2ecf20Sopenharmony_ci			 * positive offset, we need to compensate it
2278c2ecf20Sopenharmony_ci			 * with the half of the randomized range. Also
2288c2ecf20Sopenharmony_ci			 * avoid underflows.
2298c2ecf20Sopenharmony_ci			 */
2308c2ecf20Sopenharmony_ci			val += IBS_RANDOM_MAXCNT_OFFSET;
2318c2ecf20Sopenharmony_ci			if (ibs_caps & IBS_CAPS_OPCNTEXT)
2328c2ecf20Sopenharmony_ci				val = min(val, IBS_OP_MAX_CNT_EXT);
2338c2ecf20Sopenharmony_ci			else
2348c2ecf20Sopenharmony_ci				val = min(val, IBS_OP_MAX_CNT);
2358c2ecf20Sopenharmony_ci			ibs_config.max_cnt_op =
2368c2ecf20Sopenharmony_ci				(val - IBS_RANDOM_MAXCNT_OFFSET) << 4;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci		val = ((val & ~IBS_OP_MAX_CNT) << 4) | (val & IBS_OP_MAX_CNT);
2398c2ecf20Sopenharmony_ci		val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0;
2408c2ecf20Sopenharmony_ci		val |= IBS_OP_ENABLE;
2418c2ecf20Sopenharmony_ci		ibs_state.ibs_op_ctl = val;
2428c2ecf20Sopenharmony_ci		ibs_state.sample_size = IBS_OP_SIZE;
2438c2ecf20Sopenharmony_ci		if (ibs_config.branch_target) {
2448c2ecf20Sopenharmony_ci			ibs_state.branch_target = 1;
2458c2ecf20Sopenharmony_ci			ibs_state.sample_size++;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci		val = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
2488c2ecf20Sopenharmony_ci		wrmsrl(MSR_AMD64_IBSOPCTL, val);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void op_amd_stop_ibs(void)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	if (!ibs_caps)
2558c2ecf20Sopenharmony_ci		return;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (ibs_config.fetch_enabled)
2588c2ecf20Sopenharmony_ci		/* clear max count and enable */
2598c2ecf20Sopenharmony_ci		wrmsrl(MSR_AMD64_IBSFETCHCTL, 0);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (ibs_config.op_enabled)
2628c2ecf20Sopenharmony_ci		/* clear max count and enable */
2638c2ecf20Sopenharmony_ci		wrmsrl(MSR_AMD64_IBSOPCTL, 0);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void op_mux_switch_ctrl(struct op_x86_model_spec const *model,
2698c2ecf20Sopenharmony_ci			       struct op_msrs const * const msrs)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	u64 val;
2728c2ecf20Sopenharmony_ci	int i;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* enable active counters */
2758c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
2768c2ecf20Sopenharmony_ci		int virt = op_x86_phys_to_virt(i);
2778c2ecf20Sopenharmony_ci		if (!reset_value[virt])
2788c2ecf20Sopenharmony_ci			continue;
2798c2ecf20Sopenharmony_ci		rdmsrl(msrs->controls[i].addr, val);
2808c2ecf20Sopenharmony_ci		val &= model->reserved;
2818c2ecf20Sopenharmony_ci		val |= op_x86_get_ctrl(model, &counter_config[virt]);
2828c2ecf20Sopenharmony_ci		wrmsrl(msrs->controls[i].addr, val);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci#endif
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/* functions for op_amd_spec */
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void op_amd_shutdown(struct op_msrs const * const msrs)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int i;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
2958c2ecf20Sopenharmony_ci		if (!msrs->counters[i].addr)
2968c2ecf20Sopenharmony_ci			continue;
2978c2ecf20Sopenharmony_ci		release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
2988c2ecf20Sopenharmony_ci		release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int op_amd_fill_in_addresses(struct op_msrs * const msrs)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int i;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; i++) {
3078c2ecf20Sopenharmony_ci		if (!reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
3088c2ecf20Sopenharmony_ci			goto fail;
3098c2ecf20Sopenharmony_ci		if (!reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) {
3108c2ecf20Sopenharmony_ci			release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
3118c2ecf20Sopenharmony_ci			goto fail;
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci		/* both registers must be reserved */
3148c2ecf20Sopenharmony_ci		if (num_counters == AMD64_NUM_COUNTERS_CORE) {
3158c2ecf20Sopenharmony_ci			msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1);
3168c2ecf20Sopenharmony_ci			msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1);
3178c2ecf20Sopenharmony_ci		} else {
3188c2ecf20Sopenharmony_ci			msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
3198c2ecf20Sopenharmony_ci			msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci		continue;
3228c2ecf20Sopenharmony_ci	fail:
3238c2ecf20Sopenharmony_ci		if (!counter_config[i].enabled)
3248c2ecf20Sopenharmony_ci			continue;
3258c2ecf20Sopenharmony_ci		op_x86_warn_reserved(i);
3268c2ecf20Sopenharmony_ci		op_amd_shutdown(msrs);
3278c2ecf20Sopenharmony_ci		return -EBUSY;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
3348c2ecf20Sopenharmony_ci			      struct op_msrs const * const msrs)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	u64 val;
3378c2ecf20Sopenharmony_ci	int i;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* setup reset_value */
3408c2ecf20Sopenharmony_ci	for (i = 0; i < OP_MAX_COUNTER; ++i) {
3418c2ecf20Sopenharmony_ci		if (counter_config[i].enabled
3428c2ecf20Sopenharmony_ci		    && msrs->counters[op_x86_virt_to_phys(i)].addr)
3438c2ecf20Sopenharmony_ci			reset_value[i] = counter_config[i].count;
3448c2ecf20Sopenharmony_ci		else
3458c2ecf20Sopenharmony_ci			reset_value[i] = 0;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* clear all counters */
3498c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
3508c2ecf20Sopenharmony_ci		if (!msrs->controls[i].addr)
3518c2ecf20Sopenharmony_ci			continue;
3528c2ecf20Sopenharmony_ci		rdmsrl(msrs->controls[i].addr, val);
3538c2ecf20Sopenharmony_ci		if (val & ARCH_PERFMON_EVENTSEL_ENABLE)
3548c2ecf20Sopenharmony_ci			op_x86_warn_in_use(i);
3558c2ecf20Sopenharmony_ci		val &= model->reserved;
3568c2ecf20Sopenharmony_ci		wrmsrl(msrs->controls[i].addr, val);
3578c2ecf20Sopenharmony_ci		/*
3588c2ecf20Sopenharmony_ci		 * avoid a false detection of ctr overflows in NMI
3598c2ecf20Sopenharmony_ci		 * handler
3608c2ecf20Sopenharmony_ci		 */
3618c2ecf20Sopenharmony_ci		wrmsrl(msrs->counters[i].addr, -1LL);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* enable active counters */
3658c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
3668c2ecf20Sopenharmony_ci		int virt = op_x86_phys_to_virt(i);
3678c2ecf20Sopenharmony_ci		if (!reset_value[virt])
3688c2ecf20Sopenharmony_ci			continue;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		/* setup counter registers */
3718c2ecf20Sopenharmony_ci		wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		/* setup control registers */
3748c2ecf20Sopenharmony_ci		rdmsrl(msrs->controls[i].addr, val);
3758c2ecf20Sopenharmony_ci		val &= model->reserved;
3768c2ecf20Sopenharmony_ci		val |= op_x86_get_ctrl(model, &counter_config[virt]);
3778c2ecf20Sopenharmony_ci		wrmsrl(msrs->controls[i].addr, val);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int op_amd_check_ctrs(struct pt_regs * const regs,
3828c2ecf20Sopenharmony_ci			     struct op_msrs const * const msrs)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	u64 val;
3858c2ecf20Sopenharmony_ci	int i;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
3888c2ecf20Sopenharmony_ci		int virt = op_x86_phys_to_virt(i);
3898c2ecf20Sopenharmony_ci		if (!reset_value[virt])
3908c2ecf20Sopenharmony_ci			continue;
3918c2ecf20Sopenharmony_ci		rdmsrl(msrs->counters[i].addr, val);
3928c2ecf20Sopenharmony_ci		/* bit is clear if overflowed: */
3938c2ecf20Sopenharmony_ci		if (val & OP_CTR_OVERFLOW)
3948c2ecf20Sopenharmony_ci			continue;
3958c2ecf20Sopenharmony_ci		oprofile_add_sample(regs, virt);
3968c2ecf20Sopenharmony_ci		wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	op_amd_handle_ibs(regs, msrs);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* See op_model_ppro.c */
4028c2ecf20Sopenharmony_ci	return 1;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic void op_amd_start(struct op_msrs const * const msrs)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	u64 val;
4088c2ecf20Sopenharmony_ci	int i;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
4118c2ecf20Sopenharmony_ci		if (!reset_value[op_x86_phys_to_virt(i)])
4128c2ecf20Sopenharmony_ci			continue;
4138c2ecf20Sopenharmony_ci		rdmsrl(msrs->controls[i].addr, val);
4148c2ecf20Sopenharmony_ci		val |= ARCH_PERFMON_EVENTSEL_ENABLE;
4158c2ecf20Sopenharmony_ci		wrmsrl(msrs->controls[i].addr, val);
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	op_amd_start_ibs();
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic void op_amd_stop(struct op_msrs const * const msrs)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	u64 val;
4248c2ecf20Sopenharmony_ci	int i;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/*
4278c2ecf20Sopenharmony_ci	 * Subtle: stop on all counters to avoid race with setting our
4288c2ecf20Sopenharmony_ci	 * pm callback
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	for (i = 0; i < num_counters; ++i) {
4318c2ecf20Sopenharmony_ci		if (!reset_value[op_x86_phys_to_virt(i)])
4328c2ecf20Sopenharmony_ci			continue;
4338c2ecf20Sopenharmony_ci		rdmsrl(msrs->controls[i].addr, val);
4348c2ecf20Sopenharmony_ci		val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
4358c2ecf20Sopenharmony_ci		wrmsrl(msrs->controls[i].addr, val);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	op_amd_stop_ibs();
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/*
4428c2ecf20Sopenharmony_ci * check and reserve APIC extended interrupt LVT offset for IBS if
4438c2ecf20Sopenharmony_ci * available
4448c2ecf20Sopenharmony_ci */
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void init_ibs(void)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	ibs_caps = get_ibs_caps();
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!ibs_caps)
4518c2ecf20Sopenharmony_ci		return;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int (*create_arch_files)(struct dentry *root);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int setup_ibs_files(struct dentry *root)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct dentry *dir;
4618c2ecf20Sopenharmony_ci	int ret = 0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* architecture specific files */
4648c2ecf20Sopenharmony_ci	if (create_arch_files)
4658c2ecf20Sopenharmony_ci		ret = create_arch_files(root);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (ret)
4688c2ecf20Sopenharmony_ci		return ret;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (!ibs_caps)
4718c2ecf20Sopenharmony_ci		return ret;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* model specific files */
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* setup some reasonable defaults */
4768c2ecf20Sopenharmony_ci	memset(&ibs_config, 0, sizeof(ibs_config));
4778c2ecf20Sopenharmony_ci	ibs_config.max_cnt_fetch = 250000;
4788c2ecf20Sopenharmony_ci	ibs_config.max_cnt_op = 250000;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (ibs_caps & IBS_CAPS_FETCHSAM) {
4818c2ecf20Sopenharmony_ci		dir = oprofilefs_mkdir(root, "ibs_fetch");
4828c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "enable",
4838c2ecf20Sopenharmony_ci					&ibs_config.fetch_enabled);
4848c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "max_count",
4858c2ecf20Sopenharmony_ci					&ibs_config.max_cnt_fetch);
4868c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "rand_enable",
4878c2ecf20Sopenharmony_ci					&ibs_config.rand_en);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (ibs_caps & IBS_CAPS_OPSAM) {
4918c2ecf20Sopenharmony_ci		dir = oprofilefs_mkdir(root, "ibs_op");
4928c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "enable",
4938c2ecf20Sopenharmony_ci					&ibs_config.op_enabled);
4948c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "max_count",
4958c2ecf20Sopenharmony_ci					&ibs_config.max_cnt_op);
4968c2ecf20Sopenharmony_ci		if (ibs_caps & IBS_CAPS_OPCNT)
4978c2ecf20Sopenharmony_ci			oprofilefs_create_ulong(dir, "dispatched_ops",
4988c2ecf20Sopenharmony_ci						&ibs_config.dispatched_ops);
4998c2ecf20Sopenharmony_ci		if (ibs_caps & IBS_CAPS_BRNTRGT)
5008c2ecf20Sopenharmony_ci			oprofilefs_create_ulong(dir, "branch_target",
5018c2ecf20Sopenharmony_ci						&ibs_config.branch_target);
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistruct op_x86_model_spec op_amd_spec;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int op_amd_init(struct oprofile_operations *ops)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	init_ibs();
5128c2ecf20Sopenharmony_ci	create_arch_files = ops->create_files;
5138c2ecf20Sopenharmony_ci	ops->create_files = setup_ibs_files;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 == 0x15) {
5168c2ecf20Sopenharmony_ci		num_counters = AMD64_NUM_COUNTERS_CORE;
5178c2ecf20Sopenharmony_ci	} else {
5188c2ecf20Sopenharmony_ci		num_counters = AMD64_NUM_COUNTERS;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	op_amd_spec.num_counters = num_counters;
5228c2ecf20Sopenharmony_ci	op_amd_spec.num_controls = num_counters;
5238c2ecf20Sopenharmony_ci	op_amd_spec.num_virt_counters = max(num_counters, NUM_VIRT_COUNTERS);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistruct op_x86_model_spec op_amd_spec = {
5298c2ecf20Sopenharmony_ci	/* num_counters/num_controls filled in at runtime */
5308c2ecf20Sopenharmony_ci	.reserved		= MSR_AMD_EVENTSEL_RESERVED,
5318c2ecf20Sopenharmony_ci	.event_mask		= OP_EVENT_MASK,
5328c2ecf20Sopenharmony_ci	.init			= op_amd_init,
5338c2ecf20Sopenharmony_ci	.fill_in_addresses	= &op_amd_fill_in_addresses,
5348c2ecf20Sopenharmony_ci	.setup_ctrs		= &op_amd_setup_ctrs,
5358c2ecf20Sopenharmony_ci	.check_ctrs		= &op_amd_check_ctrs,
5368c2ecf20Sopenharmony_ci	.start			= &op_amd_start,
5378c2ecf20Sopenharmony_ci	.stop			= &op_amd_stop,
5388c2ecf20Sopenharmony_ci	.shutdown		= &op_amd_shutdown,
5398c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
5408c2ecf20Sopenharmony_ci	.switch_ctrl		= &op_mux_switch_ctrl,
5418c2ecf20Sopenharmony_ci#endif
5428c2ecf20Sopenharmony_ci};
543