123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- /*
- * Parisc performance counters
- * Copyright (C) 2001 Randolph Chung <tausq@debian.org>
- *
- * This code is derived, with permission, from HP/UX sources.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- /*
- * 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 <asm/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 spinlock_t perf_lock;
- 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)
- {
- int err;
- size_t image_size;
- 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 (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- if (count != sizeof(uint32_t))
- return -EIO;
- if ((err = copy_from_user(&image_type, buf, sizeof(uint32_t))) != 0)
- return err;
- /* 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 = {
- .llseek = no_llseek,
- .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 = {
- MISC_DYNAMIC_MINOR,
- PA_PERF_DEV,
- &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) {
- perf_processor_interface = CUDA_INTF;
- if (boot_cpu_data.cpu_type == pcxw2 ||
- boot_cpu_data.cpu_type == mako ||
- boot_cpu_data.cpu_type == mako2)
- bitmask_array = perf_bitmasks_piranha;
- } else {
- perf_processor_interface = UNKNOWN_INTF;
- printk("Performance monitoring counters not supported on this processor\n");
- return -ENODEV;
- }
- ret = misc_register(&perf_dev);
- if (ret) {
- printk(KERN_ERR "Performance monitoring counters: "
- "cannot register misc device.\n");
- return ret;
- }
- /* Patch the images to match the system */
- perf_patch_images();
- spin_lock_init(&perf_lock);
- /* TODO: this only lets us access the first cpu.. what to do for SMP? */
- cpu_device = per_cpu(cpu_data, 0).dev;
- printk("Performance monitoring counters enabled for %s\n",
- per_cpu(cpu_data, 0).dev->name);
- return 0;
- }
- device_initcall(perf_init);
- /*
- * perf_start_counters(void)
- *
- * Start the counters.
- */
- static void perf_start_counters(void)
- {
- /* Enable performance monitor counters */
- perf_intrigue_enable_perf_counters();
- }
- /*
- * perf_stop_counters
- *
- * Stop the performance counters and save counts
- * in a per_processor array.
- */
- static int perf_stop_counters(uint32_t *raddr)
- {
- uint64_t userbuf[MAX_RDR_WORDS];
- /* Disable performance counters */
- perf_intrigue_disable_perf_counters();
- if (perf_processor_interface == ONYX_INTF) {
- uint64_t tmp64;
- /*
- * Read the counters
- */
- if (!perf_rdr_read_ubuf(16, userbuf))
- return -13;
- /* Counter0 is bits 1398 to 1429 */
- tmp64 = (userbuf[21] << 22) & 0x00000000ffc00000;
- tmp64 |= (userbuf[22] >> 42) & 0x00000000003fffff;
- /* OR sticky0 (bit 1430) to counter0 bit 32 */
- tmp64 |= (userbuf[22] >> 10) & 0x0000000080000000;
- raddr[0] = (uint32_t)tmp64;
- /* Counter1 is bits 1431 to 1462 */
- tmp64 = (userbuf[22] >> 9) & 0x00000000ffffffff;
- /* OR sticky1 (bit 1463) to counter1 bit 32 */
- tmp64 |= (userbuf[22] << 23) & 0x0000000080000000;
- raddr[1] = (uint32_t)tmp64;
- /* Counter2 is bits 1464 to 1495 */
- tmp64 = (userbuf[22] << 24) & 0x00000000ff000000;
- tmp64 |= (userbuf[23] >> 40) & 0x0000000000ffffff;
- /* OR sticky2 (bit 1496) to counter2 bit 32 */
- tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000;
- raddr[2] = (uint32_t)tmp64;
- /* Counter3 is bits 1497 to 1528 */
- tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff;
- /* OR sticky3 (bit 1529) to counter3 bit 32 */
- tmp64 |= (userbuf[23] << 25) & 0x0000000080000000;
- raddr[3] = (uint32_t)tmp64;
- /*
- * Zero out the counters
- */
- /*
- * The counters and sticky-bits comprise the last 132 bits
- * (1398 - 1529) of RDR16 on a U chip. We'll zero these
- * out the easy way: zero out last 10 bits of dword 21,
- * all of dword 22 and 58 bits (plus 6 don't care bits) of
- * dword 23.
- */
- userbuf[21] &= 0xfffffffffffffc00ul; /* 0 to last 10 bits */
- userbuf[22] = 0;
- userbuf[23] = 0;
- /*
- * Write back the zeroed bytes + the image given
- * the read was destructive.
- */
- perf_rdr_write(16, userbuf);
- } else {
- /*
- * Read RDR-15 which contains the counters and sticky bits
- */
- if (!perf_rdr_read_ubuf(15, userbuf)) {
- return -13;
- }
- /*
- * Clear out the counters
- */
- perf_rdr_clear(15);
- /*
- * Copy the counters
- */
- raddr[0] = (uint32_t)((userbuf[0] >> 32) & 0x00000000ffffffffUL);
- raddr[1] = (uint32_t)(userbuf[0] & 0x00000000ffffffffUL);
- raddr[2] = (uint32_t)((userbuf[1] >> 32) & 0x00000000ffffffffUL);
- raddr[3] = (uint32_t)(userbuf[1] & 0x00000000ffffffffUL);
- }
- return 0;
- }
- /*
- * perf_rdr_get_entry
- *
- * Retrieve a pointer to the description of what this
- * RDR contains.
- */
- static const struct rdr_tbl_ent * perf_rdr_get_entry(uint32_t rdr_num)
- {
- if (perf_processor_interface == ONYX_INTF) {
- return &perf_rdr_tbl_U[rdr_num];
- } else {
- return &perf_rdr_tbl_W[rdr_num];
- }
- }
- /*
- * perf_rdr_read_ubuf
- *
- * Read the RDR value into the buffer specified.
- */
- static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer)
- {
- uint64_t data, data_mask = 0;
- uint32_t width, xbits, i;
- const struct rdr_tbl_ent *tentry;
- tentry = perf_rdr_get_entry(rdr_num);
- if ((width = tentry->width) == 0)
- return 0;
- /* Clear out buffer */
- i = tentry->num_words;
- while (i--) {
- buffer[i] = 0;
- }
- /* Check for bits an even number of 64 */
- if ((xbits = width & 0x03f) != 0) {
- data_mask = 1;
- data_mask <<= (64 - xbits);
- data_mask--;
- }
- /* Grab all of the data */
- i = tentry->num_words;
- while (i--) {
- if (perf_processor_interface == ONYX_INTF) {
- data = perf_rdr_shift_in_U(rdr_num, width);
- } else {
- data = perf_rdr_shift_in_W(rdr_num, width);
- }
- if (xbits) {
- buffer[i] |= (data << (64 - xbits));
- if (i) {
- buffer[i-1] |= ((data >> xbits) & data_mask);
- }
- } else {
- buffer[i] = data;
- }
- }
- return 1;
- }
- /*
- * perf_rdr_clear
- *
- * Zero out the given RDR register
- */
- static int perf_rdr_clear(uint32_t rdr_num)
- {
- const struct rdr_tbl_ent *tentry;
- int32_t i;
- tentry = perf_rdr_get_entry(rdr_num);
- if (tentry->width == 0) {
- return -1;
- }
- i = tentry->num_words;
- while (i--) {
- if (perf_processor_interface == ONYX_INTF) {
- perf_rdr_shift_out_U(rdr_num, 0UL);
- } else {
- perf_rdr_shift_out_W(rdr_num, 0UL);
- }
- }
- return 0;
- }
- /*
- * perf_write_image
- *
- * Write the given image out to the processor
- */
- static int perf_write_image(uint64_t *memaddr)
- {
- uint64_t buffer[MAX_RDR_WORDS];
- uint64_t *bptr;
- uint32_t dwords;
- const uint32_t *intrigue_rdr;
- const uint64_t *intrigue_bitmask;
- uint64_t tmp64;
- void __iomem *runway;
- const struct rdr_tbl_ent *tentry;
- int i;
- /* Clear out counters */
- if (perf_processor_interface == ONYX_INTF) {
- perf_rdr_clear(16);
- /* Toggle performance monitor */
- perf_intrigue_enable_perf_counters();
- perf_intrigue_disable_perf_counters();
- intrigue_rdr = perf_rdrs_U;
- } else {
- perf_rdr_clear(15);
- intrigue_rdr = perf_rdrs_W;
- }
- /* Write all RDRs */
- while (*intrigue_rdr != -1) {
- tentry = perf_rdr_get_entry(*intrigue_rdr);
- perf_rdr_read_ubuf(*intrigue_rdr, buffer);
- bptr = &buffer[0];
- dwords = tentry->num_words;
- if (tentry->write_control) {
- intrigue_bitmask = &bitmask_array[tentry->write_control >> 3];
- while (dwords--) {
- tmp64 = *intrigue_bitmask & *memaddr++;
- tmp64 |= (~(*intrigue_bitmask++)) & *bptr;
- *bptr++ = tmp64;
- }
- } else {
- while (dwords--) {
- *bptr++ = *memaddr++;
- }
- }
- perf_rdr_write(*intrigue_rdr, buffer);
- intrigue_rdr++;
- }
- /*
- * Now copy out the Runway stuff which is not in RDRs
- */
- if (cpu_device == NULL)
- {
- printk(KERN_ERR "write_image: cpu_device not yet initialized!\n");
- return -1;
- }
- runway = ioremap_nocache(cpu_device->hpa.start, 4096);
- if (!runway) {
- pr_err("perf_write_image: ioremap failed!\n");
- return -ENOMEM;
- }
- /* Merge intrigue bits into Runway STATUS 0 */
- tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful;
- __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul),
- runway + RUNWAY_STATUS);
- /* Write RUNWAY DEBUG registers */
- for (i = 0; i < 8; i++) {
- __raw_writeq(*memaddr++, runway + RUNWAY_DEBUG);
- }
- return 0;
- }
- /*
- * perf_rdr_write
- *
- * Write the given RDR register with the contents
- * of the given buffer.
- */
- static void perf_rdr_write(uint32_t rdr_num, uint64_t *buffer)
- {
- const struct rdr_tbl_ent *tentry;
- int32_t i;
- printk("perf_rdr_write\n");
- tentry = perf_rdr_get_entry(rdr_num);
- if (tentry->width == 0) { return; }
- i = tentry->num_words;
- while (i--) {
- if (perf_processor_interface == ONYX_INTF) {
- perf_rdr_shift_out_U(rdr_num, buffer[i]);
- } else {
- perf_rdr_shift_out_W(rdr_num, buffer[i]);
- }
- }
- printk("perf_rdr_write done\n");
- }
|