// SPDX-License-Identifier: GPL-2.0-or-later/* * Parisc performance counters * Copyright (C) 2001 Randolph Chung <tausq@debian.org> * * This code is derived, with permission, from HP/UX sources. */ /* * Edited comment from original sources: * * This driver programs the PCX-U/PCX-W performance counters * on the PA-RISC 2.0 chips. The driver keeps all images now * internally to the kernel to hopefully eliminate the possibility * of a bad image halting the CPU. Also, there are different * images for the PCX-W and later chips vs the PCX-U chips. * * Only 1 process is allowed to access the driver at any time, * so the only protection that is needed is at open and close. * A variable "perf_enabled" is used to hold the state of the * driver. The spinlock "perf_lock" is used to protect the * modification of the state during open/close operations so * multiple processes don't get into the driver simultaneously. * * This driver accesses the processor directly vs going through * the PDC INTRIGUE calls. This is done to eliminate bugs introduced * in various PDC revisions. The code is much more maintainable * and reliable this way vs having to debug on every version of PDC * on every box. */ #include <linux/capability.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/miscdevice.h>#include <linux/spinlock.h> #include <linux/uaccess.h>#include <asm/perf.h>#include <asm/parisc-device.h>#include <asm/processor.h>#include <asm/runway.h>#include <asm/io.h> /* for __raw_read() */ #include "perf_images.h" #define MAX_RDR_WORDS 24#define PERF_VERSION 2 /* derived from hpux's PI v2 interface */ /* definition of RDR regs */struct rdr_tbl_ent { uint16_t width; uint8_t num_words; uint8_t write_control;}; static int perf_processor_interface __read_mostly = UNKNOWN_INTF;static int perf_enabled __read_mostly;static DEFINE_SPINLOCK(perf_lock);static struct parisc_device *cpu_device __read_mostly; /* RDRs to write for PCX-W */static const int perf_rdrs_W[] = { 0, 1, 4, 5, 6, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 }; /* RDRs to write for PCX-U */static const int perf_rdrs_U[] = { 0, 1, 4, 5, 6, 7, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 }; /* RDR register descriptions for PCX-W */static const struct rdr_tbl_ent perf_rdr_tbl_W[] = { { 19, 1, 8 }, /* RDR 0 */ { 16, 1, 16 }, /* RDR 1 */ { 72, 2, 0 }, /* RDR 2 */ { 81, 2, 0 }, /* RDR 3 */ { 328, 6, 0 }, /* RDR 4 */ { 160, 3, 0 }, /* RDR 5 */ { 336, 6, 0 }, /* RDR 6 */ { 164, 3, 0 }, /* RDR 7 */ { 0, 0, 0 }, /* RDR 8 */ { 35, 1, 0 }, /* RDR 9 */ { 6, 1, 0 }, /* RDR 10 */ { 18, 1, 0 }, /* RDR 11 */ { 13, 1, 0 }, /* RDR 12 */ { 8, 1, 0 }, /* RDR 13 */ { 8, 1, 0 }, /* RDR 14 */ { 8, 1, 0 }, /* RDR 15 */ { 1530, 24, 0 }, /* RDR 16 */ { 16, 1, 0 }, /* RDR 17 */ { 4, 1, 0 }, /* RDR 18 */ { 0, 0, 0 }, /* RDR 19 */ { 152, 3, 24 }, /* RDR 20 */ { 152, 3, 24 }, /* RDR 21 */ { 233, 4, 48 }, /* RDR 22 */ { 233, 4, 48 }, /* RDR 23 */ { 71, 2, 0 }, /* RDR 24 */ { 71, 2, 0 }, /* RDR 25 */ { 11, 1, 0 }, /* RDR 26 */ { 18, 1, 0 }, /* RDR 27 */ { 128, 2, 0 }, /* RDR 28 */ { 0, 0, 0 }, /* RDR 29 */ { 16, 1, 0 }, /* RDR 30 */ { 16, 1, 0 }, /* RDR 31 */}; /* RDR register descriptions for PCX-U */static const struct rdr_tbl_ent perf_rdr_tbl_U[] = { { 19, 1, 8 }, /* RDR 0 */ { 32, 1, 16 }, /* RDR 1 */ { 20, 1, 0 }, /* RDR 2 */ { 0, 0, 0 }, /* RDR 3 */ { 344, 6, 0 }, /* RDR 4 */ { 176, 3, 0 }, /* RDR 5 */ { 336, 6, 0 }, /* RDR 6 */ { 0, 0, 0 }, /* RDR 7 */ { 0, 0, 0 }, /* RDR 8 */ { 0, 0, 0 }, /* RDR 9 */ { 28, 1, 0 }, /* RDR 10 */ { 33, 1, 0 }, /* RDR 11 */ { 0, 0, 0 }, /* RDR 12 */ { 230, 4, 0 }, /* RDR 13 */ { 32, 1, 0 }, /* RDR 14 */ { 128, 2, 0 }, /* RDR 15 */ { 1494, 24, 0 }, /* RDR 16 */ { 18, 1, 0 }, /* RDR 17 */ { 4, 1, 0 }, /* RDR 18 */ { 0, 0, 0 }, /* RDR 19 */ { 158, 3, 24 }, /* RDR 20 */ { 158, 3, 24 }, /* RDR 21 */ { 194, 4, 48 }, /* RDR 22 */ { 194, 4, 48 }, /* RDR 23 */ { 71, 2, 0 }, /* RDR 24 */ { 71, 2, 0 }, /* RDR 25 */ { 28, 1, 0 }, /* RDR 26 */ { 33, 1, 0 }, /* RDR 27 */ { 88, 2, 0 }, /* RDR 28 */ { 32, 1, 0 }, /* RDR 29 */ { 24, 1, 0 }, /* RDR 30 */ { 16, 1, 0 }, /* RDR 31 */}; /* * A non-zero write_control in the above tables is a byte offset into * this array. */static const uint64_t perf_bitmasks[] = { 0x0000000000000000ul, /* first dbl word must be zero */ 0xfdffe00000000000ul, /* RDR0 bitmask */ 0x003f000000000000ul, /* RDR1 bitmask */ 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (152 bits) */ 0xfffffffffffffffful, 0xfffffffc00000000ul, 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (233 bits) */ 0xfffffffffffffffful, 0xfffffffffffffffcul, 0xff00000000000000ul}; /* * Write control bitmasks for Pa-8700 processor given * some things have changed slightly. */static const uint64_t perf_bitmasks_piranha[] = { 0x0000000000000000ul, /* first dbl word must be zero */ 0xfdffe00000000000ul, /* RDR0 bitmask */ 0x003f000000000000ul, /* RDR1 bitmask */ 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (158 bits) */ 0xfffffffffffffffful, 0xfffffffc00000000ul, 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (210 bits) */ 0xfffffffffffffffful, 0xfffffffffffffffful, 0xfffc000000000000ul}; static const uint64_t *bitmask_array; /* array of bitmasks to use */ /****************************************************************************** * Function Prototypes *****************************************************************************/static int perf_config(uint32_t *image_ptr);static int perf_release(struct inode *inode, struct file *file);static int perf_open(struct inode *inode, struct file *file);static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos);static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos);static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg);static void perf_start_counters(void);static int perf_stop_counters(uint32_t *raddr);static const struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num);static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer);static int perf_rdr_clear(uint32_t rdr_num);static int perf_write_image(uint64_t *memaddr);static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer); /* External Assembly Routines */extern uint64_t perf_rdr_shift_in_W (uint32_t rdr_num, uint16_t width);extern uint64_t perf_rdr_shift_in_U (uint32_t rdr_num, uint16_t width);extern void perf_rdr_shift_out_W (uint32_t rdr_num, uint64_t buffer);extern void perf_rdr_shift_out_U (uint32_t rdr_num, uint64_t buffer);extern void perf_intrigue_enable_perf_counters (void);extern void perf_intrigue_disable_perf_counters (void); /****************************************************************************** * Function Definitions *****************************************************************************/ /* * configure: * * Configure the cpu with a given data image. First turn off the counters, * then download the image, then turn the counters back on. */static int perf_config(uint32_t *image_ptr){ long error; uint32_t raddr[4]; /* Stop the counters*/ error = perf_stop_counters(raddr); if (error != 0) { printk("perf_config: perf_stop_counters = %ld\n", error); return -EINVAL; } printk("Preparing to write image\n"); /* Write the image to the chip */ error = perf_write_image((uint64_t *)image_ptr); if (error != 0) { printk("perf_config: DOWNLOAD = %ld\n", error); return -EINVAL; } printk("Preparing to start counters\n"); /* Start the counters */ perf_start_counters(); return sizeof(uint32_t);} /* * Open the device and initialize all of its memory. The device is only * opened once, but can be "queried" by multiple processes that know its * file descriptor. */static int perf_open(struct inode *inode, struct file *file){ spin_lock(&perf_lock); if (perf_enabled) { spin_unlock(&perf_lock); return -EBUSY; } perf_enabled = 1; spin_unlock(&perf_lock); return 0;} /* * Close the device. */static int perf_release(struct inode *inode, struct file *file){ spin_lock(&perf_lock); perf_enabled = 0; spin_unlock(&perf_lock); return 0;} /* * Read does nothing for this driver */static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos){ return 0;} /* * write: * * This routine downloads the image to the chip. It must be * called on the processor that the download should happen * on. */static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ size_t image_size __maybe_unused; uint32_t image_type; uint32_t interface_type; uint32_t test; if (perf_processor_interface == ONYX_INTF) image_size = PCXU_IMAGE_SIZE; else if (perf_processor_interface == CUDA_INTF) image_size = PCXW_IMAGE_SIZE; else return -EFAULT; if (!perfmon_capable()) return -EACCES; if (count != sizeof(uint32_t)) return -EIO; if (copy_from_user(&image_type, buf, sizeof(uint32_t))) return -EFAULT; /* Get the interface type and test type */ interface_type = (image_type >> 16) & 0xffff; test = (image_type & 0xffff); /* Make sure everything makes sense */ /* First check the machine type is correct for the requested image */ if (((perf_processor_interface == CUDA_INTF) && (interface_type != CUDA_INTF)) || ((perf_processor_interface == ONYX_INTF) && (interface_type != ONYX_INTF))) return -EINVAL; /* Next check to make sure the requested image is valid */ if (((interface_type == CUDA_INTF) && (test >= MAX_CUDA_IMAGES)) || ((interface_type == ONYX_INTF) && (test >= MAX_ONYX_IMAGES))) return -EINVAL; /* Copy the image into the processor */ if (interface_type == CUDA_INTF) return perf_config(cuda_images[test]); else return perf_config(onyx_images[test]); return count;} /* * Patch the images that need to know the IVA addresses. */static void perf_patch_images(void){#if 0 /* FIXME!! *//* * NOTE: this routine is VERY specific to the current TLB image. * If the image is changed, this routine might also need to be changed. */ extern void $i_itlb_miss_2_0(); extern void $i_dtlb_miss_2_0(); extern void PA2_0_iva(); /* * We can only use the lower 32-bits, the upper 32-bits should be 0 * anyway given this is in the kernel */ uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0); uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0); uint32_t IVAaddress = (uint32_t)&PA2_0_iva; if (perf_processor_interface == ONYX_INTF) { /* clear last 2 bytes */ onyx_images[TLBMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBMISS][17] = itlb_addr; /* clear last 2 bytes */ onyx_images[TLBHANDMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBHANDMISS][17] = itlb_addr; /* clear last 2 bytes */ onyx_images[BIG_CPI][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[BIG_CPI][17] = itlb_addr; onyx_images[PANIC][15] &= 0xffffff00; /* clear last 2 bytes */ onyx_images[PANIC][15] |= (0x000000ff&((IVAaddress) >> 24)); /* set 2 bytes */ onyx_images[PANIC][16] = (IVAaddress << 8)&0xffffff00; } else if (perf_processor_interface == CUDA_INTF) { /* Cuda interface */ cuda_images[TLBMISS][16] = (cuda_images[TLBMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[TLBMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000; cuda_images[TLBHANDMISS][16] = (cuda_images[TLBHANDMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[TLBHANDMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000; cuda_images[BIG_CPI][16] = (cuda_images[BIG_CPI][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[BIG_CPI][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000; } else { /* Unknown type */ }#endif} /* * ioctl routine * All routines effect the processor that they are executed on. Thus you * must be running on the processor that you wish to change. */ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ long error_start; uint32_t raddr[4]; int error = 0; switch (cmd) { case PA_PERF_ON: /* Start the counters */ perf_start_counters(); break; case PA_PERF_OFF: error_start = perf_stop_counters(raddr); if (error_start != 0) { printk(KERN_ERR "perf_off: perf_stop_counters = %ld\n", error_start); error = -EFAULT; break; } /* copy out the Counters */ if (copy_to_user((void __user *)arg, raddr, sizeof (raddr)) != 0) { error = -EFAULT; break; } break; case PA_PERF_VERSION: /* Return the version # */ error = put_user(PERF_VERSION, (int *)arg); break; default: error = -ENOTTY; } return error;} static const struct file_operations perf_fops = { .read = perf_read, .write = perf_write, .unlocked_ioctl = perf_ioctl, .compat_ioctl = perf_ioctl, .open = perf_open, .release = perf_release}; static struct miscdevice perf_dev = { .minor = MISC_DYNAMIC_MINOR, .name = PA_PERF_DEV, .fops = &perf_fops,}; /* * Initialize the module */static int __init perf_init(void){ int ret; /* Determine correct processor interface to use */ bitmask_array = perf_bitmasks; if (boot_cpu_data.cpu_type == pcxu || boot_cpu_data.cpu_type == pcxu_) { perf_processor_interface = ONYX_INTF; } else if (boot_cpu_data.cpu_type == pcxw || boot_cpu_data.cpu_type == pcxw_ || boot_cpu_data.cpu_type == pcxw2 || boot_cpu_data.cpu_type == mako || boot_cpu_data.cpu_type == mako2) {Lines 1–500 of 837. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Parisc performance counters * Copyright (C) 2001 Randolph Chung <tausq@debian.org> * * This code is derived, with permission, from HP/UX sources. */ /* * Edited comment from original sources: * * This driver programs the PCX-U/PCX-W performance counters * on the PA-RISC 2.0 chips. The driver keeps all images now * internally to the kernel to hopefully eliminate the possibility * of a bad image halting the CPU. Also, there are different * images for the PCX-W and later chips vs the PCX-U chips. * * Only 1 process is allowed to access the driver at any time, * so the only protection that is needed is at open and close. * A variable "perf_enabled" is used to hold the state of the * driver. The spinlock "perf_lock" is used to protect the * modification of the state during open/close operations so * multiple processes don't get into the driver simultaneously. * * This driver accesses the processor directly vs going through * the PDC INTRIGUE calls. This is done to eliminate bugs introduced * in various PDC revisions. The code is much more maintainable * and reliable this way vs having to debug on every version of PDC * on every box. */ #include <linux/capability.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/miscdevice.h> #include <linux/spinlock.h> #include <linux/uaccess.h> #include <asm/perf.h> #include <asm/parisc-device.h> #include <asm/processor.h> #include <asm/runway.h> #include <asm/io.h> /* for __raw_read() */ #include "perf_images.h" #define MAX_RDR_WORDS 24 #define PERF_VERSION 2 /* derived from hpux's PI v2 interface */ /* definition of RDR regs */ struct rdr_tbl_ent { uint16_t width; uint8_t num_words; uint8_t write_control; }; static int perf_processor_interface __read_mostly = UNKNOWN_INTF; static int perf_enabled __read_mostly; static DEFINE_SPINLOCK(perf_lock); static struct parisc_device *cpu_device __read_mostly; /* RDRs to write for PCX-W */ static const int perf_rdrs_W[] = { 0, 1, 4, 5, 6, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 }; /* RDRs to write for PCX-U */ static const int perf_rdrs_U[] = { 0, 1, 4, 5, 6, 7, 16, 17, 18, 20, 21, 22, 23, 24, 25, -1 }; /* RDR register descriptions for PCX-W */ static const struct rdr_tbl_ent perf_rdr_tbl_W[] = { { 19, 1, 8 }, /* RDR 0 */ { 16, 1, 16 }, /* RDR 1 */ { 72, 2, 0 }, /* RDR 2 */ { 81, 2, 0 }, /* RDR 3 */ { 328, 6, 0 }, /* RDR 4 */ { 160, 3, 0 }, /* RDR 5 */ { 336, 6, 0 }, /* RDR 6 */ { 164, 3, 0 }, /* RDR 7 */ { 0, 0, 0 }, /* RDR 8 */ { 35, 1, 0 }, /* RDR 9 */ { 6, 1, 0 }, /* RDR 10 */ { 18, 1, 0 }, /* RDR 11 */ { 13, 1, 0 }, /* RDR 12 */ { 8, 1, 0 }, /* RDR 13 */ { 8, 1, 0 }, /* RDR 14 */ { 8, 1, 0 }, /* RDR 15 */ { 1530, 24, 0 }, /* RDR 16 */ { 16, 1, 0 }, /* RDR 17 */ { 4, 1, 0 }, /* RDR 18 */ { 0, 0, 0 }, /* RDR 19 */ { 152, 3, 24 }, /* RDR 20 */ { 152, 3, 24 }, /* RDR 21 */ { 233, 4, 48 }, /* RDR 22 */ { 233, 4, 48 }, /* RDR 23 */ { 71, 2, 0 }, /* RDR 24 */ { 71, 2, 0 }, /* RDR 25 */ { 11, 1, 0 }, /* RDR 26 */ { 18, 1, 0 }, /* RDR 27 */ { 128, 2, 0 }, /* RDR 28 */ { 0, 0, 0 }, /* RDR 29 */ { 16, 1, 0 }, /* RDR 30 */ { 16, 1, 0 }, /* RDR 31 */ }; /* RDR register descriptions for PCX-U */ static const struct rdr_tbl_ent perf_rdr_tbl_U[] = { { 19, 1, 8 }, /* RDR 0 */ { 32, 1, 16 }, /* RDR 1 */ { 20, 1, 0 }, /* RDR 2 */ { 0, 0, 0 }, /* RDR 3 */ { 344, 6, 0 }, /* RDR 4 */ { 176, 3, 0 }, /* RDR 5 */ { 336, 6, 0 }, /* RDR 6 */ { 0, 0, 0 }, /* RDR 7 */ { 0, 0, 0 }, /* RDR 8 */ { 0, 0, 0 }, /* RDR 9 */ { 28, 1, 0 }, /* RDR 10 */ { 33, 1, 0 }, /* RDR 11 */ { 0, 0, 0 }, /* RDR 12 */ { 230, 4, 0 }, /* RDR 13 */ { 32, 1, 0 }, /* RDR 14 */ { 128, 2, 0 }, /* RDR 15 */ { 1494, 24, 0 }, /* RDR 16 */ { 18, 1, 0 }, /* RDR 17 */ { 4, 1, 0 }, /* RDR 18 */ { 0, 0, 0 }, /* RDR 19 */ { 158, 3, 24 }, /* RDR 20 */ { 158, 3, 24 }, /* RDR 21 */ { 194, 4, 48 }, /* RDR 22 */ { 194, 4, 48 }, /* RDR 23 */ { 71, 2, 0 }, /* RDR 24 */ { 71, 2, 0 }, /* RDR 25 */ { 28, 1, 0 }, /* RDR 26 */ { 33, 1, 0 }, /* RDR 27 */ { 88, 2, 0 }, /* RDR 28 */ { 32, 1, 0 }, /* RDR 29 */ { 24, 1, 0 }, /* RDR 30 */ { 16, 1, 0 }, /* RDR 31 */ }; /* * A non-zero write_control in the above tables is a byte offset into * this array. */ static const uint64_t perf_bitmasks[] = { 0x0000000000000000ul, /* first dbl word must be zero */ 0xfdffe00000000000ul, /* RDR0 bitmask */ 0x003f000000000000ul, /* RDR1 bitmask */ 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (152 bits) */ 0xfffffffffffffffful, 0xfffffffc00000000ul, 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (233 bits) */ 0xfffffffffffffffful, 0xfffffffffffffffcul, 0xff00000000000000ul }; /* * Write control bitmasks for Pa-8700 processor given * some things have changed slightly. */ static const uint64_t perf_bitmasks_piranha[] = { 0x0000000000000000ul, /* first dbl word must be zero */ 0xfdffe00000000000ul, /* RDR0 bitmask */ 0x003f000000000000ul, /* RDR1 bitmask */ 0x00fffffffffffffful, /* RDR20-RDR21 bitmask (158 bits) */ 0xfffffffffffffffful, 0xfffffffc00000000ul, 0xfffffffffffffffful, /* RDR22-RDR23 bitmask (210 bits) */ 0xfffffffffffffffful, 0xfffffffffffffffful, 0xfffc000000000000ul }; static const uint64_t *bitmask_array; /* array of bitmasks to use */ /****************************************************************************** * Function Prototypes *****************************************************************************/ static int perf_config(uint32_t *image_ptr); static int perf_release(struct inode *inode, struct file *file); static int perf_open(struct inode *inode, struct file *file); static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos); static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos); static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static void perf_start_counters(void); static int perf_stop_counters(uint32_t *raddr); static const struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num); static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer); static int perf_rdr_clear(uint32_t rdr_num); static int perf_write_image(uint64_t *memaddr); static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer); /* External Assembly Routines */ extern uint64_t perf_rdr_shift_in_W (uint32_t rdr_num, uint16_t width); extern uint64_t perf_rdr_shift_in_U (uint32_t rdr_num, uint16_t width); extern void perf_rdr_shift_out_W (uint32_t rdr_num, uint64_t buffer); extern void perf_rdr_shift_out_U (uint32_t rdr_num, uint64_t buffer); extern void perf_intrigue_enable_perf_counters (void); extern void perf_intrigue_disable_perf_counters (void); /****************************************************************************** * Function Definitions *****************************************************************************/ /* * configure: * * Configure the cpu with a given data image. First turn off the counters, * then download the image, then turn the counters back on. */ static int perf_config(uint32_t *image_ptr) { long error; uint32_t raddr[4]; /* Stop the counters*/ error = perf_stop_counters(raddr); if (error != 0) { printk("perf_config: perf_stop_counters = %ld\n", error); return -EINVAL; } printk("Preparing to write image\n"); /* Write the image to the chip */ error = perf_write_image((uint64_t *)image_ptr); if (error != 0) { printk("perf_config: DOWNLOAD = %ld\n", error); return -EINVAL; } printk("Preparing to start counters\n"); /* Start the counters */ perf_start_counters(); return sizeof(uint32_t); } /* * Open the device and initialize all of its memory. The device is only * opened once, but can be "queried" by multiple processes that know its * file descriptor. */ static int perf_open(struct inode *inode, struct file *file) { spin_lock(&perf_lock); if (perf_enabled) { spin_unlock(&perf_lock); return -EBUSY; } perf_enabled = 1; spin_unlock(&perf_lock); return 0; } /* * Close the device. */ static int perf_release(struct inode *inode, struct file *file) { spin_lock(&perf_lock); perf_enabled = 0; spin_unlock(&perf_lock); return 0; } /* * Read does nothing for this driver */ static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos) { return 0; } /* * write: * * This routine downloads the image to the chip. It must be * called on the processor that the download should happen * on. */ static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { size_t image_size __maybe_unused; uint32_t image_type; uint32_t interface_type; uint32_t test; if (perf_processor_interface == ONYX_INTF) image_size = PCXU_IMAGE_SIZE; else if (perf_processor_interface == CUDA_INTF) image_size = PCXW_IMAGE_SIZE; else return -EFAULT; if (!perfmon_capable()) return -EACCES; if (count != sizeof(uint32_t)) return -EIO; if (copy_from_user(&image_type, buf, sizeof(uint32_t))) return -EFAULT; /* Get the interface type and test type */ interface_type = (image_type >> 16) & 0xffff; test = (image_type & 0xffff); /* Make sure everything makes sense */ /* First check the machine type is correct for the requested image */ if (((perf_processor_interface == CUDA_INTF) && (interface_type != CUDA_INTF)) || ((perf_processor_interface == ONYX_INTF) && (interface_type != ONYX_INTF))) return -EINVAL; /* Next check to make sure the requested image is valid */ if (((interface_type == CUDA_INTF) && (test >= MAX_CUDA_IMAGES)) || ((interface_type == ONYX_INTF) && (test >= MAX_ONYX_IMAGES))) return -EINVAL; /* Copy the image into the processor */ if (interface_type == CUDA_INTF) return perf_config(cuda_images[test]); else return perf_config(onyx_images[test]); return count; } /* * Patch the images that need to know the IVA addresses. */ static void perf_patch_images(void) { #if 0 /* FIXME!! */ /* * NOTE: this routine is VERY specific to the current TLB image. * If the image is changed, this routine might also need to be changed. */ extern void $i_itlb_miss_2_0(); extern void $i_dtlb_miss_2_0(); extern void PA2_0_iva(); /* * We can only use the lower 32-bits, the upper 32-bits should be 0 * anyway given this is in the kernel */ uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0); uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0); uint32_t IVAaddress = (uint32_t)&PA2_0_iva; if (perf_processor_interface == ONYX_INTF) { /* clear last 2 bytes */ onyx_images[TLBMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBMISS][17] = itlb_addr; /* clear last 2 bytes */ onyx_images[TLBHANDMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBHANDMISS][17] = itlb_addr; /* clear last 2 bytes */ onyx_images[BIG_CPI][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[BIG_CPI][17] = itlb_addr; onyx_images[PANIC][15] &= 0xffffff00; /* clear last 2 bytes */ onyx_images[PANIC][15] |= (0x000000ff&((IVAaddress) >> 24)); /* set 2 bytes */ onyx_images[PANIC][16] = (IVAaddress << 8)&0xffffff00; } else if (perf_processor_interface == CUDA_INTF) { /* Cuda interface */ cuda_images[TLBMISS][16] = (cuda_images[TLBMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[TLBMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000; cuda_images[TLBHANDMISS][16] = (cuda_images[TLBHANDMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[TLBHANDMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000; cuda_images[BIG_CPI][16] = (cuda_images[BIG_CPI][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); cuda_images[BIG_CPI][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000; } else { /* Unknown type */ } #endif } /* * ioctl routine * All routines effect the processor that they are executed on. Thus you * must be running on the processor that you wish to change. */ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long error_start; uint32_t raddr[4]; int error = 0; switch (cmd) { case PA_PERF_ON: /* Start the counters */ perf_start_counters(); break; case PA_PERF_OFF: error_start = perf_stop_counters(raddr); if (error_start != 0) { printk(KERN_ERR "perf_off: perf_stop_counters = %ld\n", error_start); error = -EFAULT; break; } /* copy out the Counters */ if (copy_to_user((void __user *)arg, raddr, sizeof (raddr)) != 0) { error = -EFAULT; break; } break; case PA_PERF_VERSION: /* Return the version # */ error = put_user(PERF_VERSION, (int *)arg); break; default: error = -ENOTTY; } return error; } static const struct file_operations perf_fops = { .read = perf_read, .write = perf_write, .unlocked_ioctl = perf_ioctl, .compat_ioctl = perf_ioctl, .open = perf_open, .release = perf_release }; static struct miscdevice perf_dev = { .minor = MISC_DYNAMIC_MINOR, .name = PA_PERF_DEV, .fops = &perf_fops, }; /* * Initialize the module */ static int __init perf_init(void) { int ret; /* Determine correct processor interface to use */ bitmask_array = perf_bitmasks; if (boot_cpu_data.cpu_type == pcxu || boot_cpu_data.cpu_type == pcxu_) { perf_processor_interface = ONYX_INTF; } else if (boot_cpu_data.cpu_type == pcxw || boot_cpu_data.cpu_type == pcxw_ || boot_cpu_data.cpu_type == pcxw2 || boot_cpu_data.cpu_type == mako || boot_cpu_data.cpu_type == mako2) {