// 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);
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