Commit fc17c839 authored by Suzuki K. Poulose's avatar Suzuki K. Poulose Committed by Will Deacon

arm-cci: Abstract the CCI400 PMU specific definitions

CCI400 has different event specifications for PMU, for revsion
0 and revision 1. As of now, we check the revision every single
time before using the parameters for the PMU. This patch abstracts
the details of the pmu models in a struct (cci_pmu_model) and
stores the information in cci_pmu at initialisation time, avoiding
multiple probe operations.
Tested-by: default avatarSudeep Holla <sudeep.holla@arm.com>
Acked-by: default avatarPunit Agrawal <punit.agrawal@arm.com>
Reviewed-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarSuzuki K. Poulose <suzuki.poulose@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent f6b9e83c
...@@ -79,19 +79,38 @@ static const struct of_device_id arm_cci_matches[] = { ...@@ -79,19 +79,38 @@ static const struct of_device_id arm_cci_matches[] = {
#define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */ #define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */
/* Types of interfaces that can generate events */
enum {
CCI_IF_SLAVE,
CCI_IF_MASTER,
CCI_IF_MAX,
};
struct event_range {
u32 min;
u32 max;
};
struct cci_pmu_hw_events { struct cci_pmu_hw_events {
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
raw_spinlock_t pmu_lock; raw_spinlock_t pmu_lock;
}; };
struct cci_pmu_model {
char *name;
struct event_range event_ranges[CCI_IF_MAX];
};
static struct cci_pmu_model cci_pmu_models[];
struct cci_pmu { struct cci_pmu {
void __iomem *base; void __iomem *base;
struct pmu pmu; struct pmu pmu;
int nr_irqs; int nr_irqs;
int irqs[CCI_PMU_MAX_HW_EVENTS]; int irqs[CCI_PMU_MAX_HW_EVENTS];
unsigned long active_irqs; unsigned long active_irqs;
struct pmu_port_event_ranges *port_ranges; const struct cci_pmu_model *model;
struct cci_pmu_hw_events hw_events; struct cci_pmu_hw_events hw_events;
struct platform_device *plat_device; struct platform_device *plat_device;
int num_events; int num_events;
...@@ -152,53 +171,11 @@ enum cci400_perf_events { ...@@ -152,53 +171,11 @@ enum cci400_perf_events {
#define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00 #define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00
#define CCI_REV_R1_MASTER_PORT_MAX_EV 0x11 #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",
};
static int pmu_is_valid_slave_event(u8 ev_code)
{
return pmu->port_ranges->slave_min <= ev_code &&
ev_code <= pmu->port_ranges->slave_max;
}
static int pmu_is_valid_master_event(u8 ev_code)
{
return pmu->port_ranges->master_min <= ev_code &&
ev_code <= pmu->port_ranges->master_max;
}
static int pmu_validate_hw_event(u8 hw_event) static int pmu_validate_hw_event(u8 hw_event)
{ {
u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event); u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event);
u8 ev_code = CCI_PMU_EVENT_CODE(hw_event); u8 ev_code = CCI_PMU_EVENT_CODE(hw_event);
int if_type;
switch (ev_source) { switch (ev_source) {
case CCI_PORT_S0: case CCI_PORT_S0:
...@@ -207,18 +184,22 @@ static int pmu_validate_hw_event(u8 hw_event) ...@@ -207,18 +184,22 @@ static int pmu_validate_hw_event(u8 hw_event)
case CCI_PORT_S3: case CCI_PORT_S3:
case CCI_PORT_S4: case CCI_PORT_S4:
/* Slave Interface */ /* Slave Interface */
if (pmu_is_valid_slave_event(ev_code)) if_type = CCI_IF_SLAVE;
return hw_event;
break; break;
case CCI_PORT_M0: case CCI_PORT_M0:
case CCI_PORT_M1: case CCI_PORT_M1:
case CCI_PORT_M2: case CCI_PORT_M2:
/* Master Interface */ /* Master Interface */
if (pmu_is_valid_master_event(ev_code)) if_type = CCI_IF_MASTER;
return hw_event;
break; break;
default:
return -ENOENT;
} }
if (ev_code >= pmu->model->event_ranges[if_type].min &&
ev_code <= pmu->model->event_ranges[if_type].max)
return hw_event;
return -ENOENT; return -ENOENT;
} }
...@@ -234,11 +215,9 @@ static int probe_cci_revision(void) ...@@ -234,11 +215,9 @@ static int probe_cci_revision(void)
return CCI_REV_R1; return CCI_REV_R1;
} }
static struct pmu_port_event_ranges *port_range_by_rev(void) static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev)
{ {
int rev = probe_cci_revision(); return &cci_pmu_models[probe_cci_revision()];
return &port_event_range[rev];
} }
static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx)
...@@ -816,9 +795,9 @@ static const struct attribute_group *pmu_attr_groups[] = { ...@@ -816,9 +795,9 @@ static const struct attribute_group *pmu_attr_groups[] = {
static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev)
{ {
char *name = pmu_names[probe_cci_revision()]; char *name = cci_pmu->model->name;
cci_pmu->pmu = (struct pmu) { cci_pmu->pmu = (struct pmu) {
.name = pmu_names[probe_cci_revision()], .name = cci_pmu->model->name,
.task_ctx_nr = perf_invalid_context, .task_ctx_nr = perf_invalid_context,
.pmu_enable = cci_pmu_enable, .pmu_enable = cci_pmu_enable,
.pmu_disable = cci_pmu_disable, .pmu_disable = cci_pmu_disable,
...@@ -871,6 +850,35 @@ static struct notifier_block cci_pmu_cpu_nb = { ...@@ -871,6 +850,35 @@ static struct notifier_block cci_pmu_cpu_nb = {
.priority = CPU_PRI_PERF + 1, .priority = CPU_PRI_PERF + 1,
}; };
static struct cci_pmu_model cci_pmu_models[] = {
[CCI_REV_R0] = {
.name = "CCI_400",
.event_ranges = {
[CCI_IF_SLAVE] = {
CCI_REV_R0_SLAVE_PORT_MIN_EV,
CCI_REV_R0_SLAVE_PORT_MAX_EV,
},
[CCI_IF_MASTER] = {
CCI_REV_R0_MASTER_PORT_MIN_EV,
CCI_REV_R0_MASTER_PORT_MAX_EV,
},
},
},
[CCI_REV_R1] = {
.name = "CCI_400_r1",
.event_ranges = {
[CCI_IF_SLAVE] = {
CCI_REV_R1_SLAVE_PORT_MIN_EV,
CCI_REV_R1_SLAVE_PORT_MAX_EV,
},
[CCI_IF_MASTER] = {
CCI_REV_R1_MASTER_PORT_MIN_EV,
CCI_REV_R1_MASTER_PORT_MAX_EV,
},
},
},
};
static const struct of_device_id arm_cci_pmu_matches[] = { static const struct of_device_id arm_cci_pmu_matches[] = {
{ {
.compatible = "arm,cci-400-pmu", .compatible = "arm,cci-400-pmu",
...@@ -878,6 +886,16 @@ static const struct of_device_id arm_cci_pmu_matches[] = { ...@@ -878,6 +886,16 @@ static const struct of_device_id arm_cci_pmu_matches[] = {
{}, {},
}; };
static inline const struct cci_pmu_model *get_cci_model(struct platform_device *pdev)
{
const struct of_device_id *match = of_match_node(arm_cci_pmu_matches,
pdev->dev.of_node);
if (!match)
return NULL;
return probe_cci_model(pdev);
}
static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
{ {
int i; int i;
...@@ -893,11 +911,19 @@ static int cci_pmu_probe(struct platform_device *pdev) ...@@ -893,11 +911,19 @@ static int cci_pmu_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
int i, ret, irq; int i, ret, irq;
const struct cci_pmu_model *model;
model = get_cci_model(pdev);
if (!model) {
dev_warn(&pdev->dev, "CCI PMU version not supported\n");
return -ENODEV;
}
pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL);
if (!pmu) if (!pmu)
return -ENOMEM; return -ENOMEM;
pmu->model = model;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pmu->base = devm_ioremap_resource(&pdev->dev, res); pmu->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmu->base)) if (IS_ERR(pmu->base))
...@@ -929,12 +955,6 @@ static int cci_pmu_probe(struct platform_device *pdev) ...@@ -929,12 +955,6 @@ static int cci_pmu_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
pmu->port_ranges = port_range_by_rev();
if (!pmu->port_ranges) {
dev_warn(&pdev->dev, "CCI PMU version not supported\n");
return -EINVAL;
}
raw_spin_lock_init(&pmu->hw_events.pmu_lock); raw_spin_lock_init(&pmu->hw_events.pmu_lock);
mutex_init(&pmu->reserve_mutex); mutex_init(&pmu->reserve_mutex);
atomic_set(&pmu->active_events, 0); atomic_set(&pmu->active_events, 0);
...@@ -948,6 +968,7 @@ static int cci_pmu_probe(struct platform_device *pdev) ...@@ -948,6 +968,7 @@ static int cci_pmu_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
pr_info("ARM %s PMU driver probed", pmu->model->name);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment