blob: a6ba8787db480b57196b21166dc8755b0c7de46f [file] [log] [blame]
/*
* perf_mon.c - Monhette Hill Performance Monitor driver
*
* Copyright (C) 2016 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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; version 2 of the License.
*
* 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;
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Author: Marko Bartscherer <marko.bartscherer@intel.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/mnh_perf_mon.h>
#include <linux/mnh_pcie_str.h>
#include <linux/intel-hwio.h>
#include <soc/mnh/mnh-hwio-pmon.h>
#include <soc/mnh/mnh-hwio-prmu.h>
#include <soc/mnh/mnh-hwio-scu.h>
#define DEVICE_NAME "mnh_perf_mon"
#define MAX_STR_COPY 4096
#define EMULATION 1
struct mnh_perf_mon_device *perf_mon_dev;
static uint32_t perfmon_stop_free_test(void);
static void init_perfmon(void);
static uint32_t read_emulation_setting(void)
{
uint32_t val;
struct device_node *node =
of_find_node_by_name(NULL, "chosen");
if (node && !of_property_read_u32(node, "emulation", &val))
return val;
else
return 0;
}
static void set_axi_clk(void)
{
int fsp, fbdiv, sys200, axi_clk_div, ref_clk, axi_clk;
if (read_emulation_setting() == 1)
perf_mon_dev->axi_speed = GAMMA_AXI_SPEED;
else if (read_emulation_setting() == 2)
perf_mon_dev->axi_speed = DELTA_AXI_SPEED;
else {
fsp = SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_CUR_FSP);
switch (fsp) {
case 0x0:
case 0x1:
case 0x2:
case 0x3:
/* read from LPDDR4_FSP0_SETTINGS */
fbdiv = SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_FBDIV);
sys200 = SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_SYS200_MODE);
axi_clk_div = SCU_INxf(LPDDR4_FSP_SETTING, fsp,
FSP_AXI_FABRIC_CLK_DIV);
break;
case 0x7:
fbdiv = SCU_INf(LPDDR4_REFCLK_PLL_INTGR_DIV, FBDIV);
sys200 = SCU_INf(CCU_CLK_CTL, LP4_AXI_SYS200_MODE);
axi_clk_div = SCU_INf(CCU_CLK_DIV, AXI_FABRIC_CLK_DIV);
break;
default:
sys200 = 1;
axi_clk_div = 1;
fbdiv = 1;
break;
}
ref_clk = SCU_INf(HW_STRAP, REF_CLK_SEL) ? 24000000 : 19200000;
if (sys200 == 1)
axi_clk = (200000000) / (axi_clk_div + 1);
else
axi_clk = (ref_clk * (fbdiv / 2)) / (axi_clk_div + 1);
perf_mon_dev->axi_speed = axi_clk / AXI_SPD_DIV;
}
}
static uint32_t config_prmu(void)
{
int i = 0;
while (i < PRMU_COUNT) {
if (perf_mon_dev->mode[i] == 'r') {
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_EN, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_EN, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_EN, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_EN, 1);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_EN, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_EN, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_EN, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_EN, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_EN, 0);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_EN, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_CLR, 0);
PRMU_OUTf(i+1, IRQ_EN, RD_REQ_TOTAL_BYTE_OVFL_IE, 1);
PRMU_OUTf(i+1, IRQ_EN, RD_REQ_CNT_OVFL_IE, 1);
PRMU_OUTf(i+1, IRQ_EN, RD_RESP_CNT_OVFL_IE, 1);
PRMU_OUTf(i+1, IRQ_EN, WR_REQ_TOTAL_BYTE_OVFL_IE, 0);
PRMU_OUTf(i+1, IRQ_EN, WR_REQ_CNT_OVFL_IE, 0);
PRMU_OUTf(i+1, IRQ_EN, WR_RESP_CNT_OVFL_IE, 0);
} else if (perf_mon_dev->mode[i] == 'w') {
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_EN, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_EN, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_EN, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_EN, 0);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_EN, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_EN, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_EN, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_EN, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_EN, 1);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_EN, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_CLR, 1);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RDY_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ2RESP_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_REQ_CNT_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL, RD_RESP_CNT_CLR, 0);
PRMU_OUTf(i+1, READ_CTRL,
RD_REQ_TOTAL_BYTE_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_CLR, 1);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RDY_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ2RESP_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_REQ_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL, WR_RESP_CNT_CLR, 0);
PRMU_OUTf(i+1, WRITE_CTRL,
WR_REQ_TOTAL_BYTE_CNT_CLR, 0);
PRMU_OUTf(i+1, IRQ_EN, RD_REQ_TOTAL_BYTE_OVFL_IE, 0);
PRMU_OUTf(i+1, IRQ_EN, RD_REQ_CNT_OVFL_IE, 0);
PRMU_OUTf(i+1, IRQ_EN, RD_RESP_CNT_OVFL_IE, 0);
PRMU_OUTf(i+1, IRQ_EN, WR_REQ_TOTAL_BYTE_OVFL_IE, 1);
PRMU_OUTf(i+1, IRQ_EN, WR_REQ_CNT_OVFL_IE, 1);
PRMU_OUTf(i+1, IRQ_EN, WR_RESP_CNT_OVFL_IE, 1);
} else
return 1;
i++;
}
return 0;
}
static uint32_t perfmon_start_test(uint64_t time_us)
{
uint64_t ticks;
uint64_t time = time_us * 1000000;
if ((perf_mon_dev->status == 1) || (perf_mon_dev->status == 3))
return 1;
set_axi_clk();
ticks = (perf_mon_dev->axi_speed * time_us + 999999) / 1000000;
if (((0xFFFFFFFFFFFFFFFF/time) < perf_mon_dev->axi_speed)
|| (time == 0))
return 3;
dev_dbg(perf_mon_dev->dev, "time_us %lld, ticks %lld\n", time_us, ticks);
/* Enable Clock */
SCU_OUTf(RSTC, PMON_RST, 1);
SCU_OUTf(CCU_CLK_CTL, PMON_CLKEN, 1);
SCU_OUTf(RSTC, PMON_RST, 0);
/* program registers */
init_perfmon();
PMON_OUT(SNAPSHOT_TIME_LO, LOWER(ticks));
PMON_OUT(SNAPSHOT_TIME_HI, UPPER(ticks));
if (config_prmu() == 1)
return 2;
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_CLR, 1);
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_CLR, 0);
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_EN, 1);
perf_mon_dev->error = 0;
perf_mon_dev->status = 1;
return 0;
}
static uint32_t perfmon_read_results(struct perf_result *data)
{
int i = 0;
uint32_t prmu_error = 0;
if (perf_mon_dev->status == 3)
perfmon_stop_free_test();
data->error = 0;
if ((perf_mon_dev->status == 2) || (perf_mon_dev->status == 4)) {
/*read results */
if (perf_mon_dev->error == TIMESTAMP_OVFL)
data->error = TIMESTAMP_OVFL;
else if (perf_mon_dev->error != 0)
prmu_error = (perf_mon_dev->error & 0xFF00) >> 16;
data->time_stamp = PMON_IN(TIMESTAMP_LO) +
((uint64_t)PMON_IN(TIMESTAMP_HI) << 32);
while (i < PRMU_COUNT) {
if (perf_mon_dev->mode[i] == 'r')
data->prmu[i].mode = READ_MODE;
else
data->prmu[i].mode = WRITE_MODE;
if (prmu_error == (i+1))
data->prmu[i].error = prmu_error &
~PRMU_MASK(IRQ_STS, RSVD0);
else
data->prmu[i].error = 0;
data->prmu[i].rd_req_rdy_latency =
PRMU_INf(i + 1,
RD_REQ2RDY_LATENCY_CUR_MIN, LAT_CUR);
data->prmu[i].rd_req_rdy_latency_min =
PRMU_INf(i + 1,
RD_REQ2RDY_LATENCY_CUR_MIN, LAT_MIN);
data->prmu[i].rd_req_rdy_latency_max =
PRMU_INf(i + 1,
RD_REQ2RDY_LATENCY_MAX_AVG, LAT_MAX);
data->prmu[i].rd_req_rdy_latency_avg =
PRMU_INf(i + 1,
RD_REQ2RDY_LATENCY_MAX_AVG, LAT_AVG);
data->prmu[i].rd_req_rdy_min_lat_addr =
PRMU_INf(i + 1,
RD_REQ2RDY_MIN_ADDR, ADDR);
data->prmu[i].rd_req_rdy_max_lat_addr =
PRMU_INf(i + 1, RD_REQ2RDY_MAX_ADDR, ADDR);
data->prmu[i].resp_rdy_latency =
PRMU_INf(i + 1,
REQ2RESP_LATENCY_CUR_MIN, LAT_CUR);
data->prmu[i].resp_rdy_latency_min =
PRMU_INf(i + 1,
REQ2RESP_LATENCY_CUR_MIN, LAT_MIN);
data->prmu[i].resp_rdy_latency_max =
PRMU_INf(i + 1,
REQ2RESP_LATENCY_MAX_AVG, LAT_MAX);
data->prmu[i].resp_rdy_latency_avg =
PRMU_INf(i + 1,
REQ2RESP_LATENCY_MAX_AVG, LAT_AVG);
data->prmu[i].req_resp_rdy_min_lat_addr =
PRMU_INf(i + 1, REQ2RESP_MIN_ADDR, ADDR);
data->prmu[i].req_resp_rdy_max_lat_addr =
PRMU_INf(i + 1, REQ2RESP_MAX_ADDR, ADDR);
data->prmu[i].rd_req_cnt =
PRMU_INf(i + 1, RD_REQ_CNT, CNT);
data->prmu[i].rd_resp_cnt =
PRMU_INf(i + 1, RD_RESP_CNT, CNT);
data->prmu[i].out_rd_resp_cnt =
PRMU_INf(i + 1, RD_REQ_OUTSTANDING, CNT);
data->prmu[i].rd_req_len_cnt =
PRMU_INf(i + 1, RD_REQ_TOTAL_BYTE_CNT_LO, CNT)
+ ((uint64_t)PRMU_INf(i + 1,
RD_REQ_TOTAL_BYTE_CNT_HI, CNT) << 32);
data->prmu[i].rd_bandwidth =
(uint64_t)perf_mon_dev->axi_speed *
AXI_SPD_DIV * data->prmu[i].rd_req_len_cnt /
data->time_stamp;
data->prmu[i].wr_req_rdy_latency =
PRMU_INf(i + 1,
WR_REQ2RDY_LATENCY_CUR_MIN, LAT_CUR);
data->prmu[i].wr_req_rdy_latency_min =
PRMU_INf(i + 1,
WR_REQ2RDY_LATENCY_CUR_MIN, LAT_MIN);
data->prmu[i].wr_req_rdy_latency_max =
PRMU_INf(i + 1,
WR_REQ2RDY_LATENCY_MAX_AVG, LAT_MAX);
data->prmu[i].wr_req_rdy_latency_avg =
PRMU_INf(i + 1,
WR_REQ2RDY_LATENCY_MAX_AVG, LAT_AVG);
data->prmu[i].wr_req_rdy_min_lat_addr =
PRMU_INf(i + 1, WR_REQ2RDY_MIN_ADDR, ADDR);
data->prmu[i].wr_req_rdy_max_lat_addr =
PRMU_INf(i + 1, WR_REQ2RDY_MAX_ADDR, ADDR);
data->prmu[i].wr_req_cnt =
PRMU_INf(i + 1, WR_REQ_CNT, CNT);
data->prmu[i].wr_resp_cnt =
PRMU_INf(i + 1, WR_RESP_CNT, CNT);
data->prmu[i].wr_out_cnt =
PRMU_INf(i + 1, WR_REQ_OUTSTANDING, CNT);
data->prmu[i].wr_req_len_cnt =
PRMU_INf(i + 1,
WR_REQ_TOTAL_BYTE_CNT_LO, CNT) +
((uint64_t)PRMU_INf(i + 1,
WR_REQ_TOTAL_BYTE_CNT_HI, CNT) << 32);
data->prmu[i].wr_act_req_len_cnt =
PRMU_INf(i + 1,
WR_REQ_ACTUAL_BYTE_CNT_LO, CNT) +
((uint64_t)PRMU_INf(i + 1,
WR_REQ_ACTUAL_BYTE_CNT_HI, CNT) << 32);
data->prmu[i].wr_bandwidth =
(uint64_t)perf_mon_dev->axi_speed *
AXI_SPD_DIV * data->prmu[i].wr_act_req_len_cnt /
data->time_stamp;
i++;
}
return 0;
} else
return 1;
}
static void handle_prmu_irq(uint32_t prmu)
{
uint32_t irq;
irq = PRMU_IN(prmu, IRQ_STS);
perf_mon_dev->error = (prmu<<16) + (irq & ~PRMU_MASK(IRQ_STS, RSVD0));
}
static irqreturn_t perfmon_handle_irq(int irq, void *dev_id)
{
uint32_t prmu_irq;
/* check actual interrupt content */
if (PMON_INf(IRQ_STATUS, SNAPSHOT_TIME_INTR_STS) == 1) {
if (perf_mon_dev->status == 1) {
perf_mon_dev->status = 2;
dev_err(perf_mon_dev->dev, "Perfmon timed test complete\n");
}
PMON_OUTf(IRQ_STATUS, SNAPSHOT_TIME_INTR_STS, 1);
}
if (PMON_INf(IRQ_STATUS, TIMESTAMP_OVFL_INTR_STS) == 1) {
perf_mon_dev->status = 2;
perf_mon_dev->error = TIMESTAMP_OVFL;
perfmon_stop_free_test();
dev_err(perf_mon_dev->dev, "Timestamp overflow\n");
PMON_OUTf(IRQ_STATUS, TIMESTAMP_OVFL_INTR_STS, 1);
}
/* Handle prmu IRQs */
prmu_irq = PMON_IN(PRMU_INTR);
if ((prmu_irq & PRMU_1_MASK) == PRMU_1_MASK)
handle_prmu_irq(1);
if ((prmu_irq & PRMU_2_MASK) == PRMU_2_MASK)
handle_prmu_irq(2);
if ((prmu_irq & PRMU_3_MASK) == PRMU_3_MASK)
handle_prmu_irq(3);
if ((prmu_irq & PRMU_4_MASK) == PRMU_4_MASK)
handle_prmu_irq(4);
if ((prmu_irq & PRMU_5_MASK) == PRMU_5_MASK)
handle_prmu_irq(5);
if ((prmu_irq & PRMU_6_MASK) == PRMU_6_MASK)
handle_prmu_irq(6);
if (prmu_irq != 0x0) {
perf_mon_dev->status = 2;
perf_mon_dev->error = PRMU_ERR;
perfmon_stop_free_test();
}
/* return interrupt handled */
return IRQ_HANDLED;
}
static uint32_t perfmon_start_free_test(void)
{
if ((perf_mon_dev->status == 1) || (perf_mon_dev->status == 3))
return 1;
set_axi_clk();
/* Enable Clock */
SCU_OUTf(RSTC, PMON_RST, 1);
SCU_OUTf(CCU_CLK_CTL, PMON_CLKEN, 1);
SCU_OUTf(RSTC, PMON_RST, 0);
/* program registers */
init_perfmon();
dev_err(perf_mon_dev->dev, "GC programed\n");
PMON_OUT(SNAPSHOT_TIME_LO, 0x0);
PMON_OUT(SNAPSHOT_TIME_HI, 0x0);
dev_err(perf_mon_dev->dev, "ST programmed\n");
if (config_prmu() == 1)
return 2;
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_CLR, 1);
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_CLR, 0);
PMON_OUTf(GLOBAL_CTRL, TIMESTAMP_CNT_EN, 1);
dev_err(perf_mon_dev->dev, "Test started\n");
perf_mon_dev->error = 0;
perf_mon_dev->status = 3;
return 0;
}
static uint32_t perfmon_stop_free_test(void)
{
if ((perf_mon_dev->status == 1) || (perf_mon_dev->status == 3)) {
/* program registers */
PMON_OUTf(GLOBAL_CTRL, GLBL_EN, 0);
perf_mon_dev->status = 2;
return 0;
} else
return 1;
}
static void init_perfmon(void)
{
PMON_OUTf(GLOBAL_CTRL, GLBL_EN, 1);
PMON_OUTf(IRQ_ENABLE, SNAPSHOT_TIME_INTR_EN, 1);
PMON_OUTf(IRQ_ENABLE, TIMESTAMP_OVFL_INTR_EN, 1);
PMON_OUT(PRMU_INTR, 0x3F);
PMON_OUTf(GLOBAL_CTRL, GLBL_CLR, 1);
PMON_OUTf(GLOBAL_CTRL, GLBL_CLR, 0);
}
static int config_mem(struct platform_device *pdev)
{
perf_mon_dev->memr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!perf_mon_dev->memr)
return -ENOMEM;
if (!request_mem_region(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr),
perf_mon_dev->name)) {
dev_err(&pdev->dev, "unable to request mem region\n");
return -ENOMEM;
}
perf_mon_dev->mem = ioremap_nocache(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr));
if (!perf_mon_dev->mem) {
release_mem_region(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr));
return -ENOMEM;
}
perf_mon_dev->scur = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!perf_mon_dev->scur) {
iounmap(&perf_mon_dev->mem);
release_mem_region(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr));
return -ENOMEM;
}
perf_mon_dev->scu = ioremap_nocache(perf_mon_dev->scur->start,
resource_size(perf_mon_dev->scur));
if (!perf_mon_dev->scu) {
iounmap(&perf_mon_dev->mem);
release_mem_region(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr));
return -ENOMEM;
}
return 0;
}
static int clear_mem(void)
{
iounmap(&perf_mon_dev->mem);
iounmap(&perf_mon_dev->scu);
release_mem_region(perf_mon_dev->memr->start,
resource_size(perf_mon_dev->memr));
return 0;
}
/* SYS_FS for debugging and testing */
static int print_device(struct perf_result *data, int i, char *buf)
{
char error_str[64];
char mode_str[64];
if (data->prmu[i].mode == READ_MODE)
strcpy(mode_str, "Read Mode\n");
else
strcpy(mode_str, "Write Mode\n");
if (data->error == TIMESTAMP_OVFL)
strcpy(error_str, "ERROR: TIMESTAMP Overflow\n");
else
switch (data->prmu[i].error) {
case 0:
strcpy(error_str, " ");
break;
case 0x1:
strcpy(error_str,
"ERROR: Read Request Length Counter overflow\n");
break;
case 0x2:
strcpy(error_str,
"ERROR: Read Request Counter overflow\n");
break;
case 0x4:
strcpy(error_str,
"ERROR: Read Response Counter overflow\n");
break;
case 0x8:
strcpy(error_str,
"ERROR: Write Request Length Counter overflow\n");
break;
case 0x10:
strcpy(error_str,
"ERROR: Write Request Counter overflow\n");
break;
case 0x20:
strcpy(error_str,
"ERROR: Write Response Length Counter overflow\n");
break;
default:
strcpy(error_str,
"ERROR: Multiple Counter overflow\n");
break;
}
return snprintf(buf, MAX_STR_COPY, " Timestamp %llx\n"
" %s"
" %s"
"Read Request To Ready Latency\n"
" %lx\n"
"Minimum %lx\n"
"Maximum %lx\n"
"Average %lx\n"
"Minimum Read Request Latency Address %lx\n"
"Maximum Read Request Latency Address %lx\n"
"Read/Write Request To Response Latency\n"
" %lx\n"
"Minimum %lx\n"
"Maximum %lx\n"
"Average %lx\n"
"Minimum Read/Write Response Latency Address %lx\n"
"Maximum Read/Write Response Latency Address %lx\n"
"Read Request Generated %lx\n"
"Read response Received %lx\n"
"Outstanding Read Requests %lx\n"
"Total Read Requests %llx\n"
"Total Read Bandwidth %lld bytes/second\n"
"Write Request To Ready Latency\n"
" %lx\n"
"Minimum %lx\n"
"Maximum %lx\n"
"Average %lx\n"
"Minimum Write Request Latency Address %lx\n"
"Maximum Write Request Latency Address %lx\n"
"Write Request Generated %lx\n"
"Write response Received %lx\n"
"Outstanding Write Requests %lx\n"
"Total Write Requests %llx\n"
"Actual Write Requests %llx\n"
"Total Write Bandwidth %lld bytes/second\n",
(unsigned long long) data->time_stamp,
error_str,
mode_str,
(unsigned long) data->prmu[i].rd_req_rdy_latency,
(unsigned long) data->prmu[i].rd_req_rdy_latency_min,
(unsigned long) data->prmu[i].rd_req_rdy_latency_max,
(unsigned long) data->prmu[i].rd_req_rdy_latency_avg,
(unsigned long) data->prmu[i].rd_req_rdy_min_lat_addr,
(unsigned long) data->prmu[i].rd_req_rdy_max_lat_addr,
(unsigned long) data->prmu[i].resp_rdy_latency,
(unsigned long) data->prmu[i].resp_rdy_latency_min,
(unsigned long) data->prmu[i].resp_rdy_latency_max,
(unsigned long) data->prmu[i].resp_rdy_latency_avg,
(unsigned long) data->prmu[i].req_resp_rdy_min_lat_addr,
(unsigned long) data->prmu[i].req_resp_rdy_max_lat_addr,
(unsigned long) data->prmu[i].rd_req_cnt,
(unsigned long) data->prmu[i].rd_resp_cnt,
(unsigned long) data->prmu[i].out_rd_resp_cnt,
(unsigned long long) data->prmu[i].rd_req_len_cnt,
(unsigned long long) data->prmu[i].rd_bandwidth,
(unsigned long) data->prmu[i].wr_req_rdy_latency,
(unsigned long) data->prmu[i].wr_req_rdy_latency_min,
(unsigned long) data->prmu[i].wr_req_rdy_latency_max,
(unsigned long) data->prmu[i].wr_req_rdy_latency_avg,
(unsigned long) data->prmu[i].wr_req_rdy_min_lat_addr,
(unsigned long) data->prmu[i].wr_req_rdy_max_lat_addr,
(unsigned long) data->prmu[i].wr_req_cnt,
(unsigned long) data->prmu[i].wr_resp_cnt,
(unsigned long) data->prmu[i].wr_out_cnt,
(unsigned long long) data->prmu[i].wr_req_len_cnt,
(unsigned long long) data->prmu[i].wr_act_req_len_cnt,
(unsigned long long) data->prmu[i].wr_bandwidth);
}
static ssize_t sysfs_free_measure(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
unsigned long val;
int ret;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val == 1) {
ret = perfmon_start_free_test();
if (ret == 1)
dev_err(perf_mon_dev->dev, "Measurement ongoing\n");
if (ret == 2)
dev_err(perf_mon_dev->dev, "Bad mode\n");
} else if (val == 0) {
ret = perfmon_stop_free_test();
if (ret == 1)
dev_err(perf_mon_dev->dev, "No measurement started\n");
}
return count;
}
static DEVICE_ATTR(free_test, S_IRUGO | S_IWUSR | S_IWGRP,
NULL, sysfs_free_measure);
static ssize_t sysfs_start_measure(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
unsigned long val, scale = 0;
int ret;
char *token;
token = strstr(buf, "us");
if (token) {
*token = '\0';
scale = 1;
}
if (!scale) {
token = strstr(buf, "ms");
if (token) {
*token = '\0';
scale = 1000;
}
}
if (!scale)
scale = 1000000;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val <= 0)
return -EINVAL;
ret = perfmon_start_test(val * scale);
if (ret == 1)
dev_err(perf_mon_dev->dev, "Measurement ongoing\n");
if (ret == 2)
dev_err(perf_mon_dev->dev, "Bad mode\n");
if (ret == 3)
dev_err(perf_mon_dev->dev, "Time out of range\n");
return count;
}
static DEVICE_ATTR(timed_test, S_IRUGO | S_IWUSR | S_IWGRP,
NULL, sysfs_start_measure);
static ssize_t show_sysfs_comp_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0) {
return snprintf(buf, MAX_STR_COPY, " %lx; %llx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx; %lx;"
" %lx, %lx; %lx; %lx; %lx; %lx; %lx; %llx; %lx; %lx;"
" %lx; %lx; %lx; %lx; %lx; %lx; %lx; %llx; %llx"
"\n",
(unsigned long) data.error,
(unsigned long long) data.time_stamp,
(unsigned long) data.prmu[0].error,
(unsigned long) data.prmu[0].mode,
(unsigned long) data.prmu[0].rd_req_rdy_latency,
(unsigned long) data.prmu[0].rd_req_rdy_latency_min,
(unsigned long) data.prmu[0].rd_req_rdy_latency_max,
(unsigned long) data.prmu[0].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[0].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[0].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[0].resp_rdy_latency,
(unsigned long) data.prmu[0].resp_rdy_latency_min,
(unsigned long) data.prmu[0].resp_rdy_latency_max,
(unsigned long) data.prmu[0].resp_rdy_latency_avg,
(unsigned long) data.prmu[0].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[0].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[0].rd_req_cnt,
(unsigned long) data.prmu[0].rd_resp_cnt,
(unsigned long) data.prmu[0].out_rd_resp_cnt,
(unsigned long long) data.prmu[0].rd_req_len_cnt,
(unsigned long) data.prmu[0].wr_req_rdy_latency,
(unsigned long) data.prmu[0].wr_req_rdy_latency_min,
(unsigned long) data.prmu[0].wr_req_rdy_latency_max,
(unsigned long) data.prmu[0].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[0].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[0].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[0].wr_req_cnt,
(unsigned long) data.prmu[0].wr_resp_cnt,
(unsigned long) data.prmu[0].wr_out_cnt,
(unsigned long long) data.prmu[0].wr_req_len_cnt,
(unsigned long long) data.prmu[0].wr_act_req_len_cnt,
(unsigned long) data.prmu[1].error,
(unsigned long) data.prmu[1].mode,
(unsigned long) data.prmu[1].rd_req_rdy_latency,
(unsigned long) data.prmu[1].rd_req_rdy_latency_min,
(unsigned long) data.prmu[1].rd_req_rdy_latency_max,
(unsigned long) data.prmu[1].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[1].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[1].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[1].resp_rdy_latency,
(unsigned long) data.prmu[1].resp_rdy_latency_min,
(unsigned long) data.prmu[1].resp_rdy_latency_max,
(unsigned long) data.prmu[1].resp_rdy_latency_avg,
(unsigned long) data.prmu[1].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[1].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[1].rd_req_cnt,
(unsigned long) data.prmu[1].rd_resp_cnt,
(unsigned long) data.prmu[1].out_rd_resp_cnt,
(unsigned long long) data.prmu[1].rd_req_len_cnt,
(unsigned long) data.prmu[1].wr_req_rdy_latency,
(unsigned long) data.prmu[1].wr_req_rdy_latency_min,
(unsigned long) data.prmu[1].wr_req_rdy_latency_max,
(unsigned long) data.prmu[1].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[1].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[1].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[1].wr_req_cnt,
(unsigned long) data.prmu[1].wr_resp_cnt,
(unsigned long) data.prmu[1].wr_out_cnt,
(unsigned long long) data.prmu[1].wr_req_len_cnt,
(unsigned long long) data.prmu[1].wr_act_req_len_cnt,
(unsigned long) data.prmu[2].error,
(unsigned long) data.prmu[2].mode,
(unsigned long) data.prmu[2].rd_req_rdy_latency,
(unsigned long) data.prmu[2].rd_req_rdy_latency_min,
(unsigned long) data.prmu[2].rd_req_rdy_latency_max,
(unsigned long) data.prmu[2].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[2].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[2].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[2].resp_rdy_latency,
(unsigned long) data.prmu[2].resp_rdy_latency_min,
(unsigned long) data.prmu[2].resp_rdy_latency_max,
(unsigned long) data.prmu[2].resp_rdy_latency_avg,
(unsigned long) data.prmu[2].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[2].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[2].rd_req_cnt,
(unsigned long) data.prmu[2].rd_resp_cnt,
(unsigned long) data.prmu[2].out_rd_resp_cnt,
(unsigned long long) data.prmu[2].rd_req_len_cnt,
(unsigned long) data.prmu[2].wr_req_rdy_latency,
(unsigned long) data.prmu[2].wr_req_rdy_latency_min,
(unsigned long) data.prmu[2].wr_req_rdy_latency_max,
(unsigned long) data.prmu[2].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[2].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[2].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[2].wr_req_cnt,
(unsigned long) data.prmu[2].wr_resp_cnt,
(unsigned long) data.prmu[2].wr_out_cnt,
(unsigned long long) data.prmu[2].wr_req_len_cnt,
(unsigned long long) data.prmu[2].wr_act_req_len_cnt,
(unsigned long) data.prmu[3].error,
(unsigned long) data.prmu[3].mode,
(unsigned long) data.prmu[3].rd_req_rdy_latency,
(unsigned long) data.prmu[3].rd_req_rdy_latency_min,
(unsigned long) data.prmu[3].rd_req_rdy_latency_max,
(unsigned long) data.prmu[3].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[3].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[3].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[3].resp_rdy_latency,
(unsigned long) data.prmu[3].resp_rdy_latency_min,
(unsigned long) data.prmu[3].resp_rdy_latency_max,
(unsigned long) data.prmu[3].resp_rdy_latency_avg,
(unsigned long) data.prmu[3].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[3].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[3].rd_req_cnt,
(unsigned long) data.prmu[3].rd_resp_cnt,
(unsigned long) data.prmu[3].out_rd_resp_cnt,
(unsigned long long) data.prmu[3].rd_req_len_cnt,
(unsigned long) data.prmu[3].wr_req_rdy_latency,
(unsigned long) data.prmu[3].wr_req_rdy_latency_min,
(unsigned long) data.prmu[3].wr_req_rdy_latency_max,
(unsigned long) data.prmu[3].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[3].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[3].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[3].wr_req_cnt,
(unsigned long) data.prmu[3].wr_resp_cnt,
(unsigned long) data.prmu[3].wr_out_cnt,
(unsigned long long) data.prmu[3].wr_req_len_cnt,
(unsigned long long) data.prmu[3].wr_act_req_len_cnt,
(unsigned long) data.prmu[4].error,
(unsigned long) data.prmu[4].mode,
(unsigned long) data.prmu[4].rd_req_rdy_latency,
(unsigned long) data.prmu[4].rd_req_rdy_latency_min,
(unsigned long) data.prmu[4].rd_req_rdy_latency_max,
(unsigned long) data.prmu[4].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[4].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[4].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[4].resp_rdy_latency,
(unsigned long) data.prmu[4].resp_rdy_latency_min,
(unsigned long) data.prmu[4].resp_rdy_latency_max,
(unsigned long) data.prmu[4].resp_rdy_latency_avg,
(unsigned long) data.prmu[4].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[4].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[4].rd_req_cnt,
(unsigned long) data.prmu[4].rd_resp_cnt,
(unsigned long) data.prmu[4].out_rd_resp_cnt,
(unsigned long long) data.prmu[4].rd_req_len_cnt,
(unsigned long) data.prmu[4].wr_req_rdy_latency,
(unsigned long) data.prmu[4].wr_req_rdy_latency_min,
(unsigned long) data.prmu[4].wr_req_rdy_latency_max,
(unsigned long) data.prmu[4].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[4].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[4].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[4].wr_req_cnt,
(unsigned long) data.prmu[4].wr_resp_cnt,
(unsigned long) data.prmu[4].wr_out_cnt,
(unsigned long long) data.prmu[4].wr_req_len_cnt,
(unsigned long long) data.prmu[4].wr_act_req_len_cnt,
(unsigned long) data.prmu[5].error,
(unsigned long) data.prmu[5].mode,
(unsigned long) data.prmu[5].rd_req_rdy_latency,
(unsigned long) data.prmu[5].rd_req_rdy_latency_min,
(unsigned long) data.prmu[5].rd_req_rdy_latency_max,
(unsigned long) data.prmu[5].rd_req_rdy_latency_avg,
(unsigned long) data.prmu[5].rd_req_rdy_min_lat_addr,
(unsigned long) data.prmu[5].rd_req_rdy_max_lat_addr,
(unsigned long) data.prmu[5].resp_rdy_latency,
(unsigned long) data.prmu[5].resp_rdy_latency_min,
(unsigned long) data.prmu[5].resp_rdy_latency_max,
(unsigned long) data.prmu[5].resp_rdy_latency_avg,
(unsigned long) data.prmu[5].req_resp_rdy_min_lat_addr,
(unsigned long) data.prmu[5].req_resp_rdy_max_lat_addr,
(unsigned long) data.prmu[5].rd_req_cnt,
(unsigned long) data.prmu[5].rd_resp_cnt,
(unsigned long) data.prmu[5].out_rd_resp_cnt,
(unsigned long long) data.prmu[5].rd_req_len_cnt,
(unsigned long) data.prmu[5].wr_req_rdy_latency,
(unsigned long) data.prmu[5].wr_req_rdy_latency_min,
(unsigned long) data.prmu[5].wr_req_rdy_latency_max,
(unsigned long) data.prmu[5].wr_req_rdy_latency_avg,
(unsigned long) data.prmu[5].wr_req_rdy_min_lat_addr,
(unsigned long) data.prmu[5].wr_req_rdy_max_lat_addr,
(unsigned long) data.prmu[5].wr_req_cnt,
(unsigned long) data.prmu[5].wr_resp_cnt,
(unsigned long) data.prmu[5].wr_out_cnt,
(unsigned long long) data.prmu[5].wr_req_len_cnt,
(unsigned long long) data.prmu[5].wr_act_req_len_cnt
);
} else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(comp_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_comp_results, NULL);
static ssize_t show_sysfs_ipu_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 0, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(ipu_axi_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_ipu_results, NULL);
static ssize_t show_sysfs_pcie_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 1, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(pcie_axi_master_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_pcie_results, NULL);
static ssize_t show_sysfs_a53_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 2, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(a53_axi_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_a53_results, NULL);
static ssize_t show_sysfs_axi_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 3, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(axi_master_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_axi_results, NULL);
static ssize_t show_sysfs_ddr_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 4, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(lpddr_axi_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_ddr_results, NULL);
static ssize_t show_sysfs_pcie_slave_results(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct perf_result data;
if (perfmon_read_results(&data) == 0)
return print_device(&data, 5, buf);
else
return snprintf(buf, MAX_STR_COPY, "No results available\n");
}
static DEVICE_ATTR(pcie_axi_slave_results, S_IRUGO | S_IWUSR | S_IWGRP,
show_sysfs_pcie_slave_results, NULL);
static ssize_t sysfs_test_mode(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
if ((perf_mon_dev->status == 1) || (perf_mon_dev->status == 3)) {
dev_err(perf_mon_dev->dev, "Measurement ongoing\n");
return -EINVAL;
}
if ((count == (PRMU_COUNT + 1)) && (strspn(buf, "rw") == PRMU_COUNT))
strcpy(perf_mon_dev->mode, buf);
else {
dev_err(perf_mon_dev->dev, "Wrong entry\n");
return -EINVAL;
}
return count;
}
static DEVICE_ATTR(test_mode, S_IRUGO | S_IWUSR | S_IWGRP,
NULL, sysfs_test_mode);
static struct attribute *perfmon_dev_attributes[] = {
&dev_attr_timed_test.attr,
&dev_attr_free_test.attr,
&dev_attr_test_mode.attr,
&dev_attr_comp_results.attr,
&dev_attr_ipu_axi_results.attr,
&dev_attr_pcie_axi_master_results.attr,
&dev_attr_a53_axi_results.attr,
&dev_attr_axi_master_results.attr,
&dev_attr_lpddr_axi_results.attr,
&dev_attr_pcie_axi_slave_results.attr,
NULL
};
static struct attribute_group perfmon_group = {
.name = "perfmon",
.attrs = perfmon_dev_attributes
};
static int init_sysfs(void)
{
int ret;
ret = sysfs_create_group(kernel_kobj,
&perfmon_group);
if (ret) {
dev_err(perf_mon_dev->dev, "Failed to create sysfs\n");
return -EINVAL;
}
return 0;
}
static void clean_sysfs(void)
{
sysfs_remove_group(kernel_kobj,
&perfmon_group);
}
static int mnh_perf_mon_probe(struct platform_device *pdev)
{
int err;
perf_mon_dev = kzalloc(sizeof(*perf_mon_dev), GFP_KERNEL);
if (!perf_mon_dev) {
dev_err(&pdev->dev, "Could not allocated pcie_ep_dev\n");
return -ENOMEM;
}
perf_mon_dev->dev = &pdev->dev;
strcpy(perf_mon_dev->name, DEVICE_NAME);
strcpy(perf_mon_dev->mode, "rrrrrr");
err = config_mem(pdev);
if (err)
return err;
/* Register IRQs */
perf_mon_dev->irq = platform_get_irq(pdev, 0);
perf_mon_dev->status = 0;
err = request_irq(perf_mon_dev->irq, perfmon_handle_irq,
IRQF_SHARED, DEVICE_NAME, perf_mon_dev);
if (err) {
dev_err(&pdev->dev, "Could not allocated irq\n");
clear_mem();
return -EINVAL;
}
init_sysfs();
return 0;
}
static int mnh_perf_mon_remove(struct platform_device *pdev)
{
clean_sysfs();
clear_mem();
free_irq(perf_mon_dev->irq, perf_mon_dev);
return 0;
}
/*
* of_device_id structure
*/
static const struct of_device_id mnh_perf_mon[] = {
{ .compatible = "intel, perf_mon" },
{ }
};
MODULE_DEVICE_TABLE(of, mnh_perf_mon);
/*
* Platform driver structure
*/
static struct platform_driver __refdata mnh_perf_mon_pdrv = {
.remove = mnh_perf_mon_remove,
.probe = mnh_perf_mon_probe,
.driver = {
.name = "intel, perf_mon",
.owner = THIS_MODULE,
.of_match_table = mnh_perf_mon,
},
};
module_platform_driver(mnh_perf_mon_pdrv);
MODULE_AUTHOR("Marko Bartscherer <marko.bartscherer@intel.com>");
MODULE_DESCRIPTION("Monette Hill Performance Monitor Driver");
MODULE_LICENSE("GPL");