/*
* CCI cache coherent interconnect driver
*
* Copyright (C) 2013 ARM Ltd.
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/arm-cci.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/cacheflush.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <asm/smp_plat.h>
#define DRIVER_NAME "CCI-400"
#define DRIVER_NAME_PMU DRIVER_NAME " PMU"
#define CCI_PORT_CTRL 0x0
#define CCI_CTRL_STATUS 0xc
#define CCI_ENABLE_SNOOP_REQ 0x1
#define CCI_ENABLE_DVM_REQ 0x2
#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ)
struct cci_nb_ports {
unsigned int nb_ace;
unsigned int nb_ace_lite;
};
enum cci_ace_port_type {
ACE_INVALID_PORT = 0x0,
ACE_PORT,
ACE_LITE_PORT,
};
struct cci_ace_port {
void __iomem *base;
unsigned long phys;
enum cci_ace_port_type type;
struct device_node *dn;
};
static struct cci_ace_port *ports;
static unsigned int nb_cci_ports;
static void __iomem *cci_ctrl_base;
static unsigned long cci_ctrl_phys;
#ifdef CONFIG_HW_PERF_EVENTS
#define CCI_PMCR 0x0100
#define CCI_PID2 0x0fe8
#define CCI_PMCR_CEN 0x00000001
#define CCI_PMCR_NCNT_MASK 0x0000f800
#define CCI_PMCR_NCNT_SHIFT 11
#define CCI_PID2_REV_MASK 0xf0
#define CCI_PID2_REV_SHIFT 4
/* Port ids */
#define CCI_PORT_S0 0
#define CCI_PORT_S1 1
#define CCI_PORT_S2 2
#define CCI_PORT_S3 3
#define CCI_PORT_S4 4
#define CCI_PORT_M0 5
#define CCI_PORT_M1 6
#define CCI_PORT_M2 7
#define CCI_REV_R0 0
#define CCI_REV_R1 1
#define CCI_REV_R1_PX 5
#define CCI_PMU_EVT_SEL 0x000
#define CCI_PMU_CNTR 0x004
#define CCI_PMU_CNTR_CTRL 0x008
#define CCI_PMU_OVRFLW 0x00c
#define CCI_PMU_OVRFLW_FLAG 1
#define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K)
/*
* Instead of an event id to monitor CCI cycles, a dedicated counter is
* provided. Use 0xff to represent CCI cycles and hope that no future revisions
* make use of this event in hardware.
*/
enum cci400_perf_events {
CCI_PMU_CYCLES = 0xff
};
#define CCI_PMU_EVENT_MASK 0xff
#define CCI_PMU_EVENT_SOURCE(event) ((event >> 5) & 0x7)
#define CCI_PMU_EVENT_CODE(event) (event & 0x1f)
#define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */
#define CCI_PMU_CYCLE_CNTR_IDX 0
#define CCI_PMU_CNTR0_IDX 1
#define CCI_PMU_CNTR_LAST(cci_pmu) (CCI_PMU_CYCLE_CNTR_IDX + cci_pmu->num_events - 1)
/*
* CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8
* ports and bits 4:0 are event codes. There are different event codes
* associated with each port type.
*
* Additionally, the range of events associated with the port types changed
* between Rev0 and Rev1.
*
* The constants below define the range of valid codes for each port type for
* the different revisions and are used to validate the event to be monitored.
*/
#define CCI_REV_R0_SLAVE_PORT_MIN_EV 0x00
#define CCI_REV_R0_SLAVE_PORT_MAX_EV 0x13
#define CCI_REV_R0_MASTER_PORT_MIN_EV 0x14
#define CCI_REV_R0_MASTER_PORT_MAX_EV 0x1a
#define CCI_REV_R1_SLAVE_PORT_MIN_EV 0x00
#define CCI_REV_R1_SLAVE_PORT_MAX_EV 0x14
#define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00
#define CCI_REV_R1_MASTER_PORT_MAX_EV 0x11
struct pmu_port_event_ranges {
u8 slave_min;
u8 slave_max;
u8 master_min;
u8 master_max;
};
static struct pmu_port_event_ranges port_event_range[] = {
[CCI_REV_R0] = {
.slave_min = CCI_REV_R0_SLAVE_PORT_MIN_EV,
.slave_max = CCI_REV_R0_SLAVE_PORT_MAX_EV,
.master_min = CCI_REV_R0_MASTER_PORT_MIN_EV,
.master_max = CCI_REV_R0_MASTER_PORT_MAX_EV,
},
[CCI_REV_R1] = {
.slave_min = CCI_REV_R1_SLAVE_PORT_MIN_EV,
.slave_max = CCI_REV_R1_SLAVE_PORT_MAX_EV,
.master_min = CCI_REV_R1_MASTER_PORT_MIN_EV,
.master_max = CCI_REV_R1_MASTER_PORT_MAX_EV,
},
};
/*
* Export different PMU names for the different revisions so userspace knows
* because the event ids are different
*/
static char *const pmu_names[] = {
[CCI_REV_R0] = "CCI_400",
[CCI_REV_R1] = "CCI_400_r1",
};
struct cci_pmu_drv_data {
void __iomem *base;
struct arm_pmu *cci_pmu;
int nr_irqs;
int irqs[CCI_PMU_MAX_HW_EVENTS];
unsigned long active_irqs;
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
struct pmu_port_event_ranges *port_ranges;
struct pmu_hw_events hw_events;
};
static struct cci_pmu_drv_data *pmu;
static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
{
int i;
for (i = 0; i < nr_irqs; i++)
if (irq == irqs[i])
return true;
return false;
}