/*	$OpenBSD: pctr.h,v 1.8 1997/09/16 07:52:35 deraadt Exp $	*/

/*
 * Pentium performance counter driver for OpenBSD.
 * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
 *
 * Modification and redistribution in source and binary forms is
 * permitted provided that due credit is given to the author and the
 * OpenBSD project (for instance by leaving this copyright notice
 * intact).
 */

#ifndef _I386_PCTR_H_
#define _I386_PCTR_H_

#include <sys/ioccom.h>

typedef u_quad_t pctrval;

#define PCTR_NUM 2

struct pctrst {
	u_int pctr_fn[PCTR_NUM];	/* Current settings of hardware counters */
	pctrval pctr_tsc;		/* Free-running 64-bit cycle counter */
	pctrval pctr_hwc[PCTR_NUM];	/* Values of the hardware counters */
	pctrval pctr_idl;		/* Iterations of the idle loop */
};

/* Bit values in fn fields and PIOCS ioctl's */
#define P5CTR_K 0x40          /* Monitor kernel-level events */
#define P5CTR_U 0x80          /* Monitor user-level events */
#define P5CTR_C 0x100         /* count cycles rather than events */

#define P6CTR_U  0x010000     /* Monitor user-level events */
#define P6CTR_K  0x020000     /* Monitor kernel-level events */
#define P6CTR_E  0x040000     /* Edge detect */
#define P6CTR_EN 0x400000     /* Enable counters (counter 0 only) */
#define P6CTR_I  0x800000     /* Invert counter mask */

/* Unit Mask bits */
#define P6CTR_UM_M 0x0800     /* Modified cache lines */
#define P6CTR_UM_E 0x0400     /* Exclusive cache lines */
#define P6CTR_UM_S 0x0200     /* Shared cache lines */
#define P6CTR_UM_I 0x0100     /* Invalid cache lines */
#define P6CTR_UM_MESI (P6CTR_UM_M|P6CTR_UM_E|P6CTR_UM_S|P6CTR_UM_I)
#define P6CTR_UM_A 0x2000     /* Any initiator (as opposed to self) */

#define P6CTR_CM_SHIFT 24     /* Left shift for counter mask */

/* ioctl to set which counter a device tracks. */
#define PCIOCRD _IOR('c', 1, struct pctrst)   /* Read counter value */
#define PCIOCS0 _IOW('c', 8, unsigned int)    /* Set counter 0 function */
#define PCIOCS1 _IOW('c', 9, unsigned int)    /* Set counter 1 function */

#define _PATH_PCTR "/dev/pctr"

#define __cpuid()				\
({						\
  pctrval id;					\
  __asm __volatile ("pushfl\n"			\
		    "\tpopl %%eax\n"		\
		    "\tmovl %%eax,%%ecx\n"	\
		    "\txorl %1,%%eax\n"		\
		    "\tpushl %%eax\n"		\
		    "\tpopfl\n"			\
		    "\tpushfl\n"		\
		    "\tpopl %%eax\n"		\
		    "\tpushl %%ecx\n"		\
		    "\tpopfl\n"			\
		    "\tcmpl %%eax,%%ecx\n"	\
		    "\tmovl $0,%%eax\n"		\
		    "\tje 1f\n"			\
		    "\tcpuid\n"			\
		    "\ttestl %%eax,%%eax\n"	\
		    "\tje 1f\n"			\
		    "\tmovl $1,%%eax\n"		\
		    "\tcpuid\n"			\
		    "\tjmp 2f\n"		\
		    "1:\t"			\
		    "\txorl %%eax,%%eax\n"	\
		    "\txorl %%edx,%%edx\n"	\
		    "2:\t"			\
		    : "=A" (id) : "i" (PSL_ID)	\
		    : "edx", "ecx", "ebx");	\
  id;						\
})

#define __hastsc(id) (((id) & 0x1000000000ULL) != 0ULL)
#define __hasp5ctr(id) (((id) & 0xf00) == 0x500		\
			&& (((id) & 0xf0) == 0x10	\
			    || ((id) & 0xf0) == 0x20))
#define __hasp6ctr(id) (((id) & 0xf00) == 0x600)		

#define __cpufamily() ((__cpuid() >> 8) & 0xf)

#define rdtsc()						\
({							\
  pctrval v;						\
  __asm __volatile (".byte 0xf, 0x31" : "=A" (v));	\
  v;							\
})

/* Read the performance counters (Pentium Pro only) */
#define rdpmc(ctr)				\
({						\
  pctrval v;					\
  __asm __volatile (".byte 0xf, 0x33\n"		\
		    "\tandl $0xff, %%edx"	\
		    : "=A" (v) : "c" (ctr));	\
  v;						\
})

#ifdef _KERNEL

#define CR4_TSD 0x004         /* Time stamp disable */
#define CR4_PCE 0x100         /* Performance counter enable */

#define MSR_TSC 0x10          /* MSR for TSC */
#define P5MSR_CTRSEL 0x11     /* MSR for selecting both counters on P5 */
#define P5MSR_CTR0 0x12       /* Value of Ctr0 on P5 */
#define P5MSR_CTR1 0x13       /* Value of Ctr1 on P5 */
#define P6MSR_CTRSEL0 0x186   /* MSR for programming CTR0 on P6 */
#define P6MSR_CTRSEL1 0x187   /* MSR for programming CTR0 on P6 */
#define P6MSR_CTR0 0xc1       /* Ctr0 on P6 */
#define P6MSR_CTR1 0xc2       /* Ctr1 on P6 */

#define rdmsr(msr)						\
({								\
  pctrval v;							\
  __asm __volatile (".byte 0xf, 0x32" : "=A" (v) : "c" (msr));	\
  v;								\
})

#define wrmsr(msr, v) \
     __asm __volatile (".byte 0xf, 0x30" :: "A" ((u_quad_t) (v)), "c" (msr));

#endif /* _KERNEL */
#endif /* ! _I386_PCTR_H_ */