155691f99SXu Yang // SPDX-License-Identifier: GPL-2.0
255691f99SXu Yang // Copyright 2023 NXP
355691f99SXu Yang
455691f99SXu Yang #include <linux/bitfield.h>
555691f99SXu Yang #include <linux/init.h>
655691f99SXu Yang #include <linux/interrupt.h>
755691f99SXu Yang #include <linux/io.h>
855691f99SXu Yang #include <linux/module.h>
955691f99SXu Yang #include <linux/of.h>
10918dc87bSRob Herring #include <linux/platform_device.h>
1155691f99SXu Yang #include <linux/perf_event.h>
1255691f99SXu Yang
1355691f99SXu Yang /* Performance monitor configuration */
1455691f99SXu Yang #define PMCFG1 0x00
1555691f99SXu Yang #define PMCFG1_RD_TRANS_FILT_EN BIT(31)
1655691f99SXu Yang #define PMCFG1_WR_TRANS_FILT_EN BIT(30)
1755691f99SXu Yang #define PMCFG1_RD_BT_FILT_EN BIT(29)
1855691f99SXu Yang #define PMCFG1_ID_MASK GENMASK(17, 0)
1955691f99SXu Yang
2055691f99SXu Yang #define PMCFG2 0x04
2155691f99SXu Yang #define PMCFG2_ID GENMASK(17, 0)
2255691f99SXu Yang
2355691f99SXu Yang /* Global control register affects all counters and takes priority over local control registers */
2455691f99SXu Yang #define PMGC0 0x40
2555691f99SXu Yang /* Global control register bits */
2655691f99SXu Yang #define PMGC0_FAC BIT(31)
2755691f99SXu Yang #define PMGC0_PMIE BIT(30)
2855691f99SXu Yang #define PMGC0_FCECE BIT(29)
2955691f99SXu Yang
3055691f99SXu Yang /*
3155691f99SXu Yang * 64bit counter0 exclusively dedicated to counting cycles
3255691f99SXu Yang * 32bit counters monitor counter-specific events in addition to counting reference events
3355691f99SXu Yang */
3455691f99SXu Yang #define PMLCA(n) (0x40 + 0x10 + (0x10 * n))
3555691f99SXu Yang #define PMLCB(n) (0x40 + 0x14 + (0x10 * n))
3655691f99SXu Yang #define PMC(n) (0x40 + 0x18 + (0x10 * n))
3755691f99SXu Yang /* Local control register bits */
3855691f99SXu Yang #define PMLCA_FC BIT(31)
3955691f99SXu Yang #define PMLCA_CE BIT(26)
4055691f99SXu Yang #define PMLCA_EVENT GENMASK(22, 16)
4155691f99SXu Yang
4255691f99SXu Yang #define NUM_COUNTERS 11
4355691f99SXu Yang #define CYCLES_COUNTER 0
4455691f99SXu Yang
4555691f99SXu Yang #define to_ddr_pmu(p) container_of(p, struct ddr_pmu, pmu)
4655691f99SXu Yang
4755691f99SXu Yang #define DDR_PERF_DEV_NAME "imx9_ddr"
4855691f99SXu Yang #define DDR_CPUHP_CB_NAME DDR_PERF_DEV_NAME "_perf_pmu"
4955691f99SXu Yang
5055691f99SXu Yang static DEFINE_IDA(ddr_ida);
5155691f99SXu Yang
5255691f99SXu Yang struct imx_ddr_devtype_data {
5355691f99SXu Yang const char *identifier; /* system PMU identifier for userspace */
5455691f99SXu Yang };
5555691f99SXu Yang
5655691f99SXu Yang struct ddr_pmu {
5755691f99SXu Yang struct pmu pmu;
5855691f99SXu Yang void __iomem *base;
5955691f99SXu Yang unsigned int cpu;
6055691f99SXu Yang struct hlist_node node;
6155691f99SXu Yang struct device *dev;
6255691f99SXu Yang struct perf_event *events[NUM_COUNTERS];
6355691f99SXu Yang int active_events;
6455691f99SXu Yang enum cpuhp_state cpuhp_state;
6555691f99SXu Yang const struct imx_ddr_devtype_data *devtype_data;
6655691f99SXu Yang int irq;
6755691f99SXu Yang int id;
6855691f99SXu Yang };
6955691f99SXu Yang
7055691f99SXu Yang static const struct imx_ddr_devtype_data imx93_devtype_data = {
7155691f99SXu Yang .identifier = "imx93",
7255691f99SXu Yang };
7355691f99SXu Yang
7455691f99SXu Yang static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
7555691f99SXu Yang {.compatible = "fsl,imx93-ddr-pmu", .data = &imx93_devtype_data},
7655691f99SXu Yang { /* sentinel */ }
7755691f99SXu Yang };
7855691f99SXu Yang MODULE_DEVICE_TABLE(of, imx_ddr_pmu_dt_ids);
7955691f99SXu Yang
ddr_perf_identifier_show(struct device * dev,struct device_attribute * attr,char * page)8055691f99SXu Yang static ssize_t ddr_perf_identifier_show(struct device *dev,
8155691f99SXu Yang struct device_attribute *attr,
8255691f99SXu Yang char *page)
8355691f99SXu Yang {
8455691f99SXu Yang struct ddr_pmu *pmu = dev_get_drvdata(dev);
8555691f99SXu Yang
8655691f99SXu Yang return sysfs_emit(page, "%s\n", pmu->devtype_data->identifier);
8755691f99SXu Yang }
8855691f99SXu Yang
8955691f99SXu Yang static struct device_attribute ddr_perf_identifier_attr =
9055691f99SXu Yang __ATTR(identifier, 0444, ddr_perf_identifier_show, NULL);
9155691f99SXu Yang
9255691f99SXu Yang static struct attribute *ddr_perf_identifier_attrs[] = {
9355691f99SXu Yang &ddr_perf_identifier_attr.attr,
9455691f99SXu Yang NULL,
9555691f99SXu Yang };
9655691f99SXu Yang
9755691f99SXu Yang static struct attribute_group ddr_perf_identifier_attr_group = {
9855691f99SXu Yang .attrs = ddr_perf_identifier_attrs,
9955691f99SXu Yang };
10055691f99SXu Yang
ddr_perf_cpumask_show(struct device * dev,struct device_attribute * attr,char * buf)10155691f99SXu Yang static ssize_t ddr_perf_cpumask_show(struct device *dev,
10255691f99SXu Yang struct device_attribute *attr, char *buf)
10355691f99SXu Yang {
10455691f99SXu Yang struct ddr_pmu *pmu = dev_get_drvdata(dev);
10555691f99SXu Yang
10655691f99SXu Yang return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu));
10755691f99SXu Yang }
10855691f99SXu Yang
10955691f99SXu Yang static struct device_attribute ddr_perf_cpumask_attr =
11055691f99SXu Yang __ATTR(cpumask, 0444, ddr_perf_cpumask_show, NULL);
11155691f99SXu Yang
11255691f99SXu Yang static struct attribute *ddr_perf_cpumask_attrs[] = {
11355691f99SXu Yang &ddr_perf_cpumask_attr.attr,
11455691f99SXu Yang NULL,
11555691f99SXu Yang };
11655691f99SXu Yang
11755691f99SXu Yang static const struct attribute_group ddr_perf_cpumask_attr_group = {
11855691f99SXu Yang .attrs = ddr_perf_cpumask_attrs,
11955691f99SXu Yang };
12055691f99SXu Yang
ddr_pmu_event_show(struct device * dev,struct device_attribute * attr,char * page)12155691f99SXu Yang static ssize_t ddr_pmu_event_show(struct device *dev,
12255691f99SXu Yang struct device_attribute *attr, char *page)
12355691f99SXu Yang {
12455691f99SXu Yang struct perf_pmu_events_attr *pmu_attr;
12555691f99SXu Yang
12655691f99SXu Yang pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
12755691f99SXu Yang return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);
12855691f99SXu Yang }
12955691f99SXu Yang
13055691f99SXu Yang #define IMX9_DDR_PMU_EVENT_ATTR(_name, _id) \
13155691f99SXu Yang (&((struct perf_pmu_events_attr[]) { \
13255691f99SXu Yang { .attr = __ATTR(_name, 0444, ddr_pmu_event_show, NULL),\
13355691f99SXu Yang .id = _id, } \
13455691f99SXu Yang })[0].attr.attr)
13555691f99SXu Yang
13655691f99SXu Yang static struct attribute *ddr_perf_events_attrs[] = {
13755691f99SXu Yang /* counter0 cycles event */
13855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(cycles, 0),
13955691f99SXu Yang
14055691f99SXu Yang /* reference events for all normal counters, need assert DEBUG19[21] bit */
14155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ddrc1_rmw_for_ecc, 12),
14255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_rreorder, 13),
14355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_wreorder, 14),
14455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_0, 15),
14555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_1, 16),
14655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_2, 17),
14755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_3, 18),
14855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_4, 19),
14955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_5, 22),
15055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_6, 23),
15155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_7, 24),
15255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_8, 25),
15355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_9, 26),
15455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_10, 27),
15555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_11, 28),
15655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_12, 31),
15755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_13, 59),
15855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_15, 61),
15955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_pm_29, 63),
16055691f99SXu Yang
16155691f99SXu Yang /* counter1 specific events */
16255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_0, 64),
16355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_1, 65),
16455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_2, 66),
16555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_3, 67),
16655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_4, 68),
16755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_5, 69),
16855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_6, 70),
16955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_riq_7, 71),
17055691f99SXu Yang
17155691f99SXu Yang /* counter2 specific events */
17255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_0, 64),
17355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_1, 65),
17455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_2, 66),
17555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_3, 67),
17655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_4, 68),
17755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_5, 69),
17855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_6, 70),
17955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_ld_wiq_7, 71),
18055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_empty, 72),
18155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pm_rd_trans_filt, 73),
18255691f99SXu Yang
18355691f99SXu Yang /* counter3 specific events */
18455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_0, 64),
18555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_1, 65),
18655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_2, 66),
18755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_3, 67),
18855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_4, 68),
18955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_5, 69),
19055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_6, 70),
19155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_collision_7, 71),
19255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_full, 72),
19355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pm_wr_trans_filt, 73),
19455691f99SXu Yang
19555691f99SXu Yang /* counter4 specific events */
19655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_0, 64),
19755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_1, 65),
19855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_2, 66),
19955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_3, 67),
20055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_4, 68),
20155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_5, 69),
20255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_6, 70),
20355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_row_open_7, 71),
20455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_rdq2_rmw, 72),
20555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pm_rd_beat_filt, 73),
20655691f99SXu Yang
20755691f99SXu Yang /* counter5 specific events */
20855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_0, 64),
20955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_1, 65),
21055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_2, 66),
21155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_3, 67),
21255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_4, 68),
21355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_5, 69),
21455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_6, 70),
21555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_start_7, 71),
21655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_rdq1, 72),
21755691f99SXu Yang
21855691f99SXu Yang /* counter6 specific events */
21955691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(ddrc_qx_valid_end_0, 64),
22055691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_rdq2, 72),
22155691f99SXu Yang
22255691f99SXu Yang /* counter7 specific events */
22355691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_1_2_full, 64),
22455691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_wrq0, 65),
22555691f99SXu Yang
22655691f99SXu Yang /* counter8 specific events */
22755691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_bias_switched, 64),
22855691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_1_4_full, 65),
22955691f99SXu Yang
23055691f99SXu Yang /* counter9 specific events */
23155691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_wrq1, 65),
23255691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_3_4_full, 66),
23355691f99SXu Yang
23455691f99SXu Yang /* counter10 specific events */
23555691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_misc_mrk, 65),
23655691f99SXu Yang IMX9_DDR_PMU_EVENT_ATTR(eddrtq_pmon_ld_rdq0, 66),
23755691f99SXu Yang NULL,
23855691f99SXu Yang };
23955691f99SXu Yang
24055691f99SXu Yang static const struct attribute_group ddr_perf_events_attr_group = {
24155691f99SXu Yang .name = "events",
24255691f99SXu Yang .attrs = ddr_perf_events_attrs,
24355691f99SXu Yang };
24455691f99SXu Yang
24555691f99SXu Yang PMU_FORMAT_ATTR(event, "config:0-7");
24655691f99SXu Yang PMU_FORMAT_ATTR(counter, "config:8-15");
24755691f99SXu Yang PMU_FORMAT_ATTR(axi_id, "config1:0-17");
24855691f99SXu Yang PMU_FORMAT_ATTR(axi_mask, "config2:0-17");
24955691f99SXu Yang
25055691f99SXu Yang static struct attribute *ddr_perf_format_attrs[] = {
25155691f99SXu Yang &format_attr_event.attr,
25255691f99SXu Yang &format_attr_counter.attr,
25355691f99SXu Yang &format_attr_axi_id.attr,
25455691f99SXu Yang &format_attr_axi_mask.attr,
25555691f99SXu Yang NULL,
25655691f99SXu Yang };
25755691f99SXu Yang
25855691f99SXu Yang static const struct attribute_group ddr_perf_format_attr_group = {
25955691f99SXu Yang .name = "format",
26055691f99SXu Yang .attrs = ddr_perf_format_attrs,
26155691f99SXu Yang };
26255691f99SXu Yang
26355691f99SXu Yang static const struct attribute_group *attr_groups[] = {
26455691f99SXu Yang &ddr_perf_identifier_attr_group,
26555691f99SXu Yang &ddr_perf_cpumask_attr_group,
26655691f99SXu Yang &ddr_perf_events_attr_group,
26755691f99SXu Yang &ddr_perf_format_attr_group,
26855691f99SXu Yang NULL,
26955691f99SXu Yang };
27055691f99SXu Yang
ddr_perf_clear_counter(struct ddr_pmu * pmu,int counter)27155691f99SXu Yang static void ddr_perf_clear_counter(struct ddr_pmu *pmu, int counter)
27255691f99SXu Yang {
27355691f99SXu Yang if (counter == CYCLES_COUNTER) {
27455691f99SXu Yang writel(0, pmu->base + PMC(counter) + 0x4);
27555691f99SXu Yang writel(0, pmu->base + PMC(counter));
27655691f99SXu Yang } else {
27755691f99SXu Yang writel(0, pmu->base + PMC(counter));
27855691f99SXu Yang }
27955691f99SXu Yang }
28055691f99SXu Yang
ddr_perf_read_counter(struct ddr_pmu * pmu,int counter)28155691f99SXu Yang static u64 ddr_perf_read_counter(struct ddr_pmu *pmu, int counter)
28255691f99SXu Yang {
28355691f99SXu Yang u32 val_lower, val_upper;
28455691f99SXu Yang u64 val;
28555691f99SXu Yang
28655691f99SXu Yang if (counter != CYCLES_COUNTER) {
28755691f99SXu Yang val = readl_relaxed(pmu->base + PMC(counter));
28855691f99SXu Yang goto out;
28955691f99SXu Yang }
29055691f99SXu Yang
29155691f99SXu Yang /* special handling for reading 64bit cycle counter */
29255691f99SXu Yang do {
29355691f99SXu Yang val_upper = readl_relaxed(pmu->base + PMC(counter) + 0x4);
29455691f99SXu Yang val_lower = readl_relaxed(pmu->base + PMC(counter));
29555691f99SXu Yang } while (val_upper != readl_relaxed(pmu->base + PMC(counter) + 0x4));
29655691f99SXu Yang
29755691f99SXu Yang val = val_upper;
29855691f99SXu Yang val = (val << 32);
29955691f99SXu Yang val |= val_lower;
30055691f99SXu Yang out:
30155691f99SXu Yang return val;
30255691f99SXu Yang }
30355691f99SXu Yang
ddr_perf_counter_global_config(struct ddr_pmu * pmu,bool enable)30455691f99SXu Yang static void ddr_perf_counter_global_config(struct ddr_pmu *pmu, bool enable)
30555691f99SXu Yang {
30655691f99SXu Yang u32 ctrl;
30755691f99SXu Yang
30855691f99SXu Yang ctrl = readl_relaxed(pmu->base + PMGC0);
30955691f99SXu Yang
31055691f99SXu Yang if (enable) {
31155691f99SXu Yang /*
31255691f99SXu Yang * The performance monitor must be reset before event counting
31355691f99SXu Yang * sequences. The performance monitor can be reset by first freezing
31455691f99SXu Yang * one or more counters and then clearing the freeze condition to
31555691f99SXu Yang * allow the counters to count according to the settings in the
31655691f99SXu Yang * performance monitor registers. Counters can be frozen individually
31755691f99SXu Yang * by setting PMLCAn[FC] bits, or simultaneously by setting PMGC0[FAC].
31855691f99SXu Yang * Simply clearing these freeze bits will then allow the performance
31955691f99SXu Yang * monitor to begin counting based on the register settings.
32055691f99SXu Yang */
32155691f99SXu Yang ctrl |= PMGC0_FAC;
32255691f99SXu Yang writel(ctrl, pmu->base + PMGC0);
32355691f99SXu Yang
32455691f99SXu Yang /*
32555691f99SXu Yang * Freeze all counters disabled, interrupt enabled, and freeze
32655691f99SXu Yang * counters on condition enabled.
32755691f99SXu Yang */
32855691f99SXu Yang ctrl &= ~PMGC0_FAC;
32955691f99SXu Yang ctrl |= PMGC0_PMIE | PMGC0_FCECE;
33055691f99SXu Yang writel(ctrl, pmu->base + PMGC0);
33155691f99SXu Yang } else {
33255691f99SXu Yang ctrl |= PMGC0_FAC;
33355691f99SXu Yang ctrl &= ~(PMGC0_PMIE | PMGC0_FCECE);
33455691f99SXu Yang writel(ctrl, pmu->base + PMGC0);
33555691f99SXu Yang }
33655691f99SXu Yang }
33755691f99SXu Yang
ddr_perf_counter_local_config(struct ddr_pmu * pmu,int config,int counter,bool enable)33855691f99SXu Yang static void ddr_perf_counter_local_config(struct ddr_pmu *pmu, int config,
33955691f99SXu Yang int counter, bool enable)
34055691f99SXu Yang {
34155691f99SXu Yang u32 ctrl_a;
34255691f99SXu Yang
34355691f99SXu Yang ctrl_a = readl_relaxed(pmu->base + PMLCA(counter));
34455691f99SXu Yang
34555691f99SXu Yang if (enable) {
34655691f99SXu Yang ctrl_a |= PMLCA_FC;
34755691f99SXu Yang writel(ctrl_a, pmu->base + PMLCA(counter));
34855691f99SXu Yang
34955691f99SXu Yang ddr_perf_clear_counter(pmu, counter);
35055691f99SXu Yang
35155691f99SXu Yang /* Freeze counter disabled, condition enabled, and program event.*/
35255691f99SXu Yang ctrl_a &= ~PMLCA_FC;
35355691f99SXu Yang ctrl_a |= PMLCA_CE;
35455691f99SXu Yang ctrl_a &= ~FIELD_PREP(PMLCA_EVENT, 0x7F);
35555691f99SXu Yang ctrl_a |= FIELD_PREP(PMLCA_EVENT, (config & 0x000000FF));
35655691f99SXu Yang writel(ctrl_a, pmu->base + PMLCA(counter));
35755691f99SXu Yang } else {
35855691f99SXu Yang /* Freeze counter. */
35955691f99SXu Yang ctrl_a |= PMLCA_FC;
36055691f99SXu Yang writel(ctrl_a, pmu->base + PMLCA(counter));
36155691f99SXu Yang }
36255691f99SXu Yang }
36355691f99SXu Yang
ddr_perf_monitor_config(struct ddr_pmu * pmu,int cfg,int cfg1,int cfg2)36455691f99SXu Yang static void ddr_perf_monitor_config(struct ddr_pmu *pmu, int cfg, int cfg1, int cfg2)
36555691f99SXu Yang {
36655691f99SXu Yang u32 pmcfg1, pmcfg2;
36755691f99SXu Yang int event, counter;
36855691f99SXu Yang
36955691f99SXu Yang event = cfg & 0x000000FF;
37055691f99SXu Yang counter = (cfg & 0x0000FF00) >> 8;
37155691f99SXu Yang
37255691f99SXu Yang pmcfg1 = readl_relaxed(pmu->base + PMCFG1);
37355691f99SXu Yang
37455691f99SXu Yang if (counter == 2 && event == 73)
37555691f99SXu Yang pmcfg1 |= PMCFG1_RD_TRANS_FILT_EN;
37655691f99SXu Yang else if (counter == 2 && event != 73)
37755691f99SXu Yang pmcfg1 &= ~PMCFG1_RD_TRANS_FILT_EN;
37855691f99SXu Yang
37955691f99SXu Yang if (counter == 3 && event == 73)
38055691f99SXu Yang pmcfg1 |= PMCFG1_WR_TRANS_FILT_EN;
38155691f99SXu Yang else if (counter == 3 && event != 73)
38255691f99SXu Yang pmcfg1 &= ~PMCFG1_WR_TRANS_FILT_EN;
38355691f99SXu Yang
38455691f99SXu Yang if (counter == 4 && event == 73)
38555691f99SXu Yang pmcfg1 |= PMCFG1_RD_BT_FILT_EN;
38655691f99SXu Yang else if (counter == 4 && event != 73)
38755691f99SXu Yang pmcfg1 &= ~PMCFG1_RD_BT_FILT_EN;
38855691f99SXu Yang
38955691f99SXu Yang pmcfg1 &= ~FIELD_PREP(PMCFG1_ID_MASK, 0x3FFFF);
39055691f99SXu Yang pmcfg1 |= FIELD_PREP(PMCFG1_ID_MASK, cfg2);
39155691f99SXu Yang writel(pmcfg1, pmu->base + PMCFG1);
39255691f99SXu Yang
39355691f99SXu Yang pmcfg2 = readl_relaxed(pmu->base + PMCFG2);
39455691f99SXu Yang pmcfg2 &= ~FIELD_PREP(PMCFG2_ID, 0x3FFFF);
39555691f99SXu Yang pmcfg2 |= FIELD_PREP(PMCFG2_ID, cfg1);
39655691f99SXu Yang writel(pmcfg2, pmu->base + PMCFG2);
39755691f99SXu Yang }
39855691f99SXu Yang
ddr_perf_event_update(struct perf_event * event)39955691f99SXu Yang static void ddr_perf_event_update(struct perf_event *event)
40055691f99SXu Yang {
40155691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
40255691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
40355691f99SXu Yang int counter = hwc->idx;
40455691f99SXu Yang u64 new_raw_count;
40555691f99SXu Yang
40655691f99SXu Yang new_raw_count = ddr_perf_read_counter(pmu, counter);
40755691f99SXu Yang local64_add(new_raw_count, &event->count);
40855691f99SXu Yang
40955691f99SXu Yang /* clear counter's value every time */
41055691f99SXu Yang ddr_perf_clear_counter(pmu, counter);
41155691f99SXu Yang }
41255691f99SXu Yang
ddr_perf_event_init(struct perf_event * event)41355691f99SXu Yang static int ddr_perf_event_init(struct perf_event *event)
41455691f99SXu Yang {
41555691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
41655691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
41755691f99SXu Yang struct perf_event *sibling;
41855691f99SXu Yang
41955691f99SXu Yang if (event->attr.type != event->pmu->type)
42055691f99SXu Yang return -ENOENT;
42155691f99SXu Yang
42255691f99SXu Yang if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
42355691f99SXu Yang return -EOPNOTSUPP;
42455691f99SXu Yang
42555691f99SXu Yang if (event->cpu < 0) {
42655691f99SXu Yang dev_warn(pmu->dev, "Can't provide per-task data!\n");
42755691f99SXu Yang return -EOPNOTSUPP;
42855691f99SXu Yang }
42955691f99SXu Yang
43055691f99SXu Yang /*
43155691f99SXu Yang * We must NOT create groups containing mixed PMUs, although software
43255691f99SXu Yang * events are acceptable (for example to create a CCN group
43355691f99SXu Yang * periodically read when a hrtimer aka cpu-clock leader triggers).
43455691f99SXu Yang */
43555691f99SXu Yang if (event->group_leader->pmu != event->pmu &&
43655691f99SXu Yang !is_software_event(event->group_leader))
43755691f99SXu Yang return -EINVAL;
43855691f99SXu Yang
43955691f99SXu Yang for_each_sibling_event(sibling, event->group_leader) {
44055691f99SXu Yang if (sibling->pmu != event->pmu &&
44155691f99SXu Yang !is_software_event(sibling))
44255691f99SXu Yang return -EINVAL;
44355691f99SXu Yang }
44455691f99SXu Yang
44555691f99SXu Yang event->cpu = pmu->cpu;
44655691f99SXu Yang hwc->idx = -1;
44755691f99SXu Yang
44855691f99SXu Yang return 0;
44955691f99SXu Yang }
45055691f99SXu Yang
ddr_perf_event_start(struct perf_event * event,int flags)45155691f99SXu Yang static void ddr_perf_event_start(struct perf_event *event, int flags)
45255691f99SXu Yang {
45355691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
45455691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
45555691f99SXu Yang int counter = hwc->idx;
45655691f99SXu Yang
45755691f99SXu Yang local64_set(&hwc->prev_count, 0);
45855691f99SXu Yang
45955691f99SXu Yang ddr_perf_counter_local_config(pmu, event->attr.config, counter, true);
46055691f99SXu Yang hwc->state = 0;
46155691f99SXu Yang }
46255691f99SXu Yang
ddr_perf_event_add(struct perf_event * event,int flags)46355691f99SXu Yang static int ddr_perf_event_add(struct perf_event *event, int flags)
46455691f99SXu Yang {
46555691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
46655691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
46755691f99SXu Yang int cfg = event->attr.config;
46855691f99SXu Yang int cfg1 = event->attr.config1;
46955691f99SXu Yang int cfg2 = event->attr.config2;
47055691f99SXu Yang int counter;
47155691f99SXu Yang
47255691f99SXu Yang counter = (cfg & 0x0000FF00) >> 8;
47355691f99SXu Yang
47455691f99SXu Yang pmu->events[counter] = event;
47555691f99SXu Yang pmu->active_events++;
47655691f99SXu Yang hwc->idx = counter;
47755691f99SXu Yang hwc->state |= PERF_HES_STOPPED;
47855691f99SXu Yang
47955691f99SXu Yang /* read trans, write trans, read beat */
48055691f99SXu Yang ddr_perf_monitor_config(pmu, cfg, cfg1, cfg2);
48155691f99SXu Yang
482*0bcd599aSXu Yang if (flags & PERF_EF_START)
483*0bcd599aSXu Yang ddr_perf_event_start(event, flags);
484*0bcd599aSXu Yang
48555691f99SXu Yang return 0;
48655691f99SXu Yang }
48755691f99SXu Yang
ddr_perf_event_stop(struct perf_event * event,int flags)48855691f99SXu Yang static void ddr_perf_event_stop(struct perf_event *event, int flags)
48955691f99SXu Yang {
49055691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
49155691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
49255691f99SXu Yang int counter = hwc->idx;
49355691f99SXu Yang
49455691f99SXu Yang ddr_perf_counter_local_config(pmu, event->attr.config, counter, false);
49555691f99SXu Yang ddr_perf_event_update(event);
49655691f99SXu Yang
49755691f99SXu Yang hwc->state |= PERF_HES_STOPPED;
49855691f99SXu Yang }
49955691f99SXu Yang
ddr_perf_event_del(struct perf_event * event,int flags)50055691f99SXu Yang static void ddr_perf_event_del(struct perf_event *event, int flags)
50155691f99SXu Yang {
50255691f99SXu Yang struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
50355691f99SXu Yang struct hw_perf_event *hwc = &event->hw;
50455691f99SXu Yang
50555691f99SXu Yang ddr_perf_event_stop(event, PERF_EF_UPDATE);
50655691f99SXu Yang
50755691f99SXu Yang pmu->active_events--;
50855691f99SXu Yang hwc->idx = -1;
50955691f99SXu Yang }
51055691f99SXu Yang
ddr_perf_pmu_enable(struct pmu * pmu)51155691f99SXu Yang static void ddr_perf_pmu_enable(struct pmu *pmu)
51255691f99SXu Yang {
51355691f99SXu Yang struct ddr_pmu *ddr_pmu = to_ddr_pmu(pmu);
51455691f99SXu Yang
51555691f99SXu Yang ddr_perf_counter_global_config(ddr_pmu, true);
51655691f99SXu Yang }
51755691f99SXu Yang
ddr_perf_pmu_disable(struct pmu * pmu)51855691f99SXu Yang static void ddr_perf_pmu_disable(struct pmu *pmu)
51955691f99SXu Yang {
52055691f99SXu Yang struct ddr_pmu *ddr_pmu = to_ddr_pmu(pmu);
52155691f99SXu Yang
52255691f99SXu Yang ddr_perf_counter_global_config(ddr_pmu, false);
52355691f99SXu Yang }
52455691f99SXu Yang
ddr_perf_init(struct ddr_pmu * pmu,void __iomem * base,struct device * dev)52555691f99SXu Yang static void ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
52655691f99SXu Yang struct device *dev)
52755691f99SXu Yang {
52855691f99SXu Yang *pmu = (struct ddr_pmu) {
52955691f99SXu Yang .pmu = (struct pmu) {
53055691f99SXu Yang .module = THIS_MODULE,
53155691f99SXu Yang .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
53255691f99SXu Yang .task_ctx_nr = perf_invalid_context,
53355691f99SXu Yang .attr_groups = attr_groups,
53455691f99SXu Yang .event_init = ddr_perf_event_init,
53555691f99SXu Yang .add = ddr_perf_event_add,
53655691f99SXu Yang .del = ddr_perf_event_del,
53755691f99SXu Yang .start = ddr_perf_event_start,
53855691f99SXu Yang .stop = ddr_perf_event_stop,
53955691f99SXu Yang .read = ddr_perf_event_update,
54055691f99SXu Yang .pmu_enable = ddr_perf_pmu_enable,
54155691f99SXu Yang .pmu_disable = ddr_perf_pmu_disable,
54255691f99SXu Yang },
54355691f99SXu Yang .base = base,
54455691f99SXu Yang .dev = dev,
54555691f99SXu Yang };
54655691f99SXu Yang }
54755691f99SXu Yang
ddr_perf_irq_handler(int irq,void * p)54855691f99SXu Yang static irqreturn_t ddr_perf_irq_handler(int irq, void *p)
54955691f99SXu Yang {
55055691f99SXu Yang struct ddr_pmu *pmu = (struct ddr_pmu *)p;
55155691f99SXu Yang struct perf_event *event;
55255691f99SXu Yang int i;
55355691f99SXu Yang
55455691f99SXu Yang /*
55555691f99SXu Yang * Counters can generate an interrupt on an overflow when msb of a
55655691f99SXu Yang * counter changes from 0 to 1. For the interrupt to be signalled,
55755691f99SXu Yang * below condition mush be satisfied:
55855691f99SXu Yang * PMGC0[PMIE] = 1, PMGC0[FCECE] = 1, PMLCAn[CE] = 1
55955691f99SXu Yang * When an interrupt is signalled, PMGC0[FAC] is set by hardware and
56055691f99SXu Yang * all of the registers are frozen.
56155691f99SXu Yang * Software can clear the interrupt condition by resetting the performance
56255691f99SXu Yang * monitor and clearing the most significant bit of the counter that
56355691f99SXu Yang * generate the overflow.
56455691f99SXu Yang */
56555691f99SXu Yang for (i = 0; i < NUM_COUNTERS; i++) {
56655691f99SXu Yang if (!pmu->events[i])
56755691f99SXu Yang continue;
56855691f99SXu Yang
56955691f99SXu Yang event = pmu->events[i];
57055691f99SXu Yang
57155691f99SXu Yang ddr_perf_event_update(event);
57255691f99SXu Yang }
57355691f99SXu Yang
57455691f99SXu Yang ddr_perf_counter_global_config(pmu, true);
57555691f99SXu Yang
57655691f99SXu Yang return IRQ_HANDLED;
57755691f99SXu Yang }
57855691f99SXu Yang
ddr_perf_offline_cpu(unsigned int cpu,struct hlist_node * node)57955691f99SXu Yang static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
58055691f99SXu Yang {
58155691f99SXu Yang struct ddr_pmu *pmu = hlist_entry_safe(node, struct ddr_pmu, node);
58255691f99SXu Yang int target;
58355691f99SXu Yang
58455691f99SXu Yang if (cpu != pmu->cpu)
58555691f99SXu Yang return 0;
58655691f99SXu Yang
58755691f99SXu Yang target = cpumask_any_but(cpu_online_mask, cpu);
58855691f99SXu Yang if (target >= nr_cpu_ids)
58955691f99SXu Yang return 0;
59055691f99SXu Yang
59155691f99SXu Yang perf_pmu_migrate_context(&pmu->pmu, cpu, target);
59255691f99SXu Yang pmu->cpu = target;
59355691f99SXu Yang
59455691f99SXu Yang WARN_ON(irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu)));
59555691f99SXu Yang
59655691f99SXu Yang return 0;
59755691f99SXu Yang }
59855691f99SXu Yang
ddr_perf_probe(struct platform_device * pdev)59955691f99SXu Yang static int ddr_perf_probe(struct platform_device *pdev)
60055691f99SXu Yang {
60155691f99SXu Yang struct ddr_pmu *pmu;
60255691f99SXu Yang void __iomem *base;
60355691f99SXu Yang int ret, irq;
60455691f99SXu Yang char *name;
60555691f99SXu Yang
60655691f99SXu Yang base = devm_platform_ioremap_resource(pdev, 0);
60755691f99SXu Yang if (IS_ERR(base))
60855691f99SXu Yang return PTR_ERR(base);
60955691f99SXu Yang
61055691f99SXu Yang pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL);
61155691f99SXu Yang if (!pmu)
61255691f99SXu Yang return -ENOMEM;
61355691f99SXu Yang
61455691f99SXu Yang ddr_perf_init(pmu, base, &pdev->dev);
61555691f99SXu Yang
61655691f99SXu Yang pmu->devtype_data = of_device_get_match_data(&pdev->dev);
61755691f99SXu Yang
61855691f99SXu Yang platform_set_drvdata(pdev, pmu);
61955691f99SXu Yang
62055691f99SXu Yang pmu->id = ida_simple_get(&ddr_ida, 0, 0, GFP_KERNEL);
62155691f99SXu Yang name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DDR_PERF_DEV_NAME "%d", pmu->id);
62255691f99SXu Yang if (!name) {
62355691f99SXu Yang ret = -ENOMEM;
62455691f99SXu Yang goto format_string_err;
62555691f99SXu Yang }
62655691f99SXu Yang
62755691f99SXu Yang pmu->cpu = raw_smp_processor_id();
62855691f99SXu Yang ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DDR_CPUHP_CB_NAME,
62955691f99SXu Yang NULL, ddr_perf_offline_cpu);
63055691f99SXu Yang if (ret < 0) {
63155691f99SXu Yang dev_err(&pdev->dev, "Failed to add callbacks for multi state\n");
63255691f99SXu Yang goto cpuhp_state_err;
63355691f99SXu Yang }
63455691f99SXu Yang pmu->cpuhp_state = ret;
63555691f99SXu Yang
63655691f99SXu Yang /* Register the pmu instance for cpu hotplug */
63755691f99SXu Yang ret = cpuhp_state_add_instance_nocalls(pmu->cpuhp_state, &pmu->node);
63855691f99SXu Yang if (ret) {
63955691f99SXu Yang dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
64055691f99SXu Yang goto cpuhp_instance_err;
64155691f99SXu Yang }
64255691f99SXu Yang
64355691f99SXu Yang /* Request irq */
64455691f99SXu Yang irq = platform_get_irq(pdev, 0);
64555691f99SXu Yang if (irq < 0) {
64655691f99SXu Yang ret = irq;
64755691f99SXu Yang goto ddr_perf_err;
64855691f99SXu Yang }
64955691f99SXu Yang
65055691f99SXu Yang ret = devm_request_irq(&pdev->dev, irq, ddr_perf_irq_handler,
65155691f99SXu Yang IRQF_NOBALANCING | IRQF_NO_THREAD,
65255691f99SXu Yang DDR_CPUHP_CB_NAME, pmu);
65355691f99SXu Yang if (ret < 0) {
65455691f99SXu Yang dev_err(&pdev->dev, "Request irq failed: %d", ret);
65555691f99SXu Yang goto ddr_perf_err;
65655691f99SXu Yang }
65755691f99SXu Yang
65855691f99SXu Yang pmu->irq = irq;
65955691f99SXu Yang ret = irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu));
66055691f99SXu Yang if (ret) {
66155691f99SXu Yang dev_err(pmu->dev, "Failed to set interrupt affinity\n");
66255691f99SXu Yang goto ddr_perf_err;
66355691f99SXu Yang }
66455691f99SXu Yang
66555691f99SXu Yang ret = perf_pmu_register(&pmu->pmu, name, -1);
66655691f99SXu Yang if (ret)
66755691f99SXu Yang goto ddr_perf_err;
66855691f99SXu Yang
66955691f99SXu Yang return 0;
67055691f99SXu Yang
67155691f99SXu Yang ddr_perf_err:
67255691f99SXu Yang cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node);
67355691f99SXu Yang cpuhp_instance_err:
67455691f99SXu Yang cpuhp_remove_multi_state(pmu->cpuhp_state);
67555691f99SXu Yang cpuhp_state_err:
67655691f99SXu Yang format_string_err:
67755691f99SXu Yang ida_simple_remove(&ddr_ida, pmu->id);
67855691f99SXu Yang dev_warn(&pdev->dev, "i.MX9 DDR Perf PMU failed (%d), disabled\n", ret);
67955691f99SXu Yang return ret;
68055691f99SXu Yang }
68155691f99SXu Yang
ddr_perf_remove(struct platform_device * pdev)68255691f99SXu Yang static int ddr_perf_remove(struct platform_device *pdev)
68355691f99SXu Yang {
68455691f99SXu Yang struct ddr_pmu *pmu = platform_get_drvdata(pdev);
68555691f99SXu Yang
68655691f99SXu Yang cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node);
68755691f99SXu Yang cpuhp_remove_multi_state(pmu->cpuhp_state);
68855691f99SXu Yang
68955691f99SXu Yang perf_pmu_unregister(&pmu->pmu);
69055691f99SXu Yang
69155691f99SXu Yang ida_simple_remove(&ddr_ida, pmu->id);
69255691f99SXu Yang
69355691f99SXu Yang return 0;
69455691f99SXu Yang }
69555691f99SXu Yang
69655691f99SXu Yang static struct platform_driver imx_ddr_pmu_driver = {
69755691f99SXu Yang .driver = {
69855691f99SXu Yang .name = "imx9-ddr-pmu",
69955691f99SXu Yang .of_match_table = imx_ddr_pmu_dt_ids,
70055691f99SXu Yang .suppress_bind_attrs = true,
70155691f99SXu Yang },
70255691f99SXu Yang .probe = ddr_perf_probe,
70355691f99SXu Yang .remove = ddr_perf_remove,
70455691f99SXu Yang };
70555691f99SXu Yang module_platform_driver(imx_ddr_pmu_driver);
70655691f99SXu Yang
70755691f99SXu Yang MODULE_AUTHOR("Xu Yang <xu.yang_2@nxp.com>");
70855691f99SXu Yang MODULE_LICENSE("GPL v2");
70955691f99SXu Yang MODULE_DESCRIPTION("DDRC PerfMon for i.MX9 SoCs");
710