From patchwork Tue Jul 12 06:36:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiucheng Xu X-Patchwork-Id: 12914535 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id DD574C433EF for ; Tue, 12 Jul 2022 06:37:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:Date:Subject:CC :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=uSLMLdafvVvIpTg5NM+SLyOPEZo2trr634oQv6TfV54=; b=ndvzg25oGe7d1O yzryHwY2A9ZxBGUpKJu++1XqpcfXQIK/oNH7G1A0EZCCx/yupygdteQTCCrEi1oZGd8OKYMxmM/06 8qtZeFaBEIhIDy7j+A8jkIByqlXpJUV/dCrHYbMJsd3Cf7imJ+E+gmm+KEWcQSV1DveMfwBl3sBun QENVoTwv989nESFn3n9n92bjfC2gMY2D9JqTUtRjFXAGxoeDJc0nCygZ1opYimXJg6PK4L+x2QNzA 74sNr3h5JAdSZyMbpMML8a6kyi39cNSATEkb55NF/X/w1QoLZj1hVRNsMI5dWjHxLT9hFWpkLQyKN 15Yb2p6nh1sCms9Mxw5Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9WM-0083KH-Un; Tue, 12 Jul 2022 06:37:22 +0000 Received: from mail-sh.amlogic.com ([58.32.228.43]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9W8-0083Fb-CC; Tue, 12 Jul 2022 06:37:12 +0000 Received: from droid01-xa.amlogic.com (10.88.11.200) by mail-sh.amlogic.com (10.18.11.5) with Microsoft SMTP Server id 15.1.2176.14; Tue, 12 Jul 2022 14:36:57 +0800 From: Jiucheng Xu To: , , , CC: Rob Herring , Krzysztof Kozlowski , Will Deacon , Mark Rutland , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl , Chris Healy , Jiucheng Xu Subject: [PATCH 1/4] perf/amlogic: Add support for Amlogic meson G12 SoC DDR PMU driver Date: Tue, 12 Jul 2022 14:36:38 +0800 Message-ID: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-Originating-IP: [10.88.11.200] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220711_233709_078532_A3EC27A8 X-CRM114-Status: GOOD ( 30.20 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org This patch adds support Amlogic meson G12 series SoC DDR bandwidth PMU driver framework and interfaces. The PMU not only can monitor the total DDR bandwidth, but also the bandwidth which is from individual IP module. Example usage: $ perf stat -a -e aml_ddr_bw/total_rw_bytes/ -I 1000 sleep 10 - or - $ perf stat -a -e \ aml_ddr_bw/total_rw_bytes/,\ aml_ddr_bw/chan_1_rw_bytes,arm=1/ -I 1000 \ sleep 10 g12 SoC support 4 channels to monitor DDR bandwidth simultaneously. Each channel can monitor up to 4 IP modules simultaneously. For Instance, If you want to get the sum of DDR bandwidth from CPU, GPU, USB3.0 and VDEC. You can use the following command parameters to display. $ perf stat -a -e \ aml_ddr_bw/chan_2_rw_bytes,arm=1,gpu=1,usb3_0=1,nna=1/ -I 1000 \ sleep 10 Other events are supported, and advertised via perf list. Signed-off-by: Jiucheng Xu --- MAINTAINERS | 7 + drivers/perf/Kconfig | 2 + drivers/perf/Makefile | 1 + drivers/perf/amlogic/Kconfig | 11 + drivers/perf/amlogic/Makefile | 3 + drivers/perf/amlogic/aml_ddr_pmu.c | 725 +++++++++++++++++++++++++ drivers/perf/amlogic/aml_ddr_pmu_g12.c | 398 ++++++++++++++ include/soc/amlogic/aml_ddr_pmu.h | 63 +++ 8 files changed, 1210 insertions(+) create mode 100644 drivers/perf/amlogic/Kconfig create mode 100644 drivers/perf/amlogic/Makefile create mode 100644 drivers/perf/amlogic/aml_ddr_pmu.c create mode 100644 drivers/perf/amlogic/aml_ddr_pmu_g12.c create mode 100644 include/soc/amlogic/aml_ddr_pmu.h diff --git a/MAINTAINERS b/MAINTAINERS index f679152bdbad..cb6ee59a4f44 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1050,6 +1050,13 @@ S: Maintained F: Documentation/hid/amd-sfh* F: drivers/hid/amd-sfh-hid/ +AMLOGIC DDR PMU DRIVER +M: Jiucheng Xu +S: Supported +W: http://www.amlogic.com +F: drivers/perf/amlogic/ +F: include/soc/amlogic/ + AMPHION VPU CODEC V4L2 DRIVER M: Ming Qian M: Shijie Qin diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index 1e2d69453771..68200c798128 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -192,4 +192,6 @@ config MARVELL_CN10K_DDR_PMU Enable perf support for Marvell DDR Performance monitoring event on CN10K platform. +source "drivers/perf/amlogic/Kconfig" + endmenu diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index 57a279c61df5..ef82b27e36d0 100644 --- a/drivers/perf/Makefile +++ b/drivers/perf/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_AML_DDR_PMU) += amlogic/ obj-$(CONFIG_ARM_CCI_PMU) += arm-cci.o obj-$(CONFIG_ARM_CCN) += arm-ccn.o obj-$(CONFIG_ARM_CMN) += arm-cmn.o diff --git a/drivers/perf/amlogic/Kconfig b/drivers/perf/amlogic/Kconfig new file mode 100644 index 000000000000..d88bd8371aba --- /dev/null +++ b/drivers/perf/amlogic/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config AML_DDR_PMU + tristate "Amlogic DDR Bandwidth Performance Monitor" + depends on ARCH_MESON || COMPILE_TEST + default n + help + Provides support for the DDR performance monitor + in Amlogic SoCs, which can give information about + memory throughput and other related events. It + supports multiple channels to monitor the memory + bandwidth simultaneously. diff --git a/drivers/perf/amlogic/Makefile b/drivers/perf/amlogic/Makefile new file mode 100644 index 000000000000..4dc11620292b --- /dev/null +++ b/drivers/perf/amlogic/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_AML_DDR_PMU) += aml_ddr_pmu.o \ + aml_ddr_pmu_g12.o diff --git a/drivers/perf/amlogic/aml_ddr_pmu.c b/drivers/perf/amlogic/aml_ddr_pmu.c new file mode 100644 index 000000000000..67956d2a4cfc --- /dev/null +++ b/drivers/perf/amlogic/aml_ddr_pmu.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Amlogic, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DDR_PERF_DEV_NAME "aml_ddr_bw" +#define MAX_AXI_PORTS_OF_CHANNEL 4 /* A DMC channel can monitor max 4 axi ports */ +#define MAX_BITMAP 64 /* u64 event.attr.config1, 64 bits */ + +#define to_ddr_pmu(p) container_of(p, struct ddr_pmu, pmu) + +#define hw_info_to_pmu(p) container_of(p, struct ddr_pmu, info) + +struct ddr_pmu { + struct pmu pmu; + struct dmc_hw_info info; + struct dmc_counter counters; /* save counters from hw */ + bool pmu_enabled; + spinlock_t lock; /* protect hw counter */ + struct device *dev; + char *name; + struct hlist_node node; + enum cpuhp_state cpuhp_state; + int cpu; /* for cpu hotplug */ +}; + +static inline void dmc_pmu_enable(struct ddr_pmu *pmu) +{ + spin_lock(&pmu->lock); + if (!pmu->pmu_enabled && pmu->info.ops->enable) + pmu->info.ops->enable(&pmu->info); + + pmu->pmu_enabled = true; + spin_unlock(&pmu->lock); +} + +static inline void dmc_pmu_disable(struct ddr_pmu *pmu) +{ + spin_lock(&pmu->lock); + if (pmu->pmu_enabled && pmu->info.ops->disable) + pmu->info.ops->disable(&pmu->info); + + pmu->pmu_enabled = false; + spin_unlock(&pmu->lock); +} + +static inline void ddr_cnt_accumulate(struct ddr_pmu *pmu, + struct dmc_counter *c) +{ + struct dmc_counter *cnter = &pmu->counters; + int chann_nr = pmu->info.chann_nr; + int i; + + cnter->all_cnt += c->all_cnt; + cnter->all_req += c->all_req; + + for (i = 0; i < chann_nr; i++) + cnter->channel_cnt[i] += c->channel_cnt[i]; +} + +static void aml_ddr_config_axi_id(struct ddr_pmu *pmu, int axi_id, int chann) +{ + if (pmu->info.ops->config_axi_id) { + spin_lock(&pmu->lock); + pmu->info.ops->config_axi_id(&pmu->info, axi_id, chann); + spin_unlock(&pmu->lock); + } +} + +static void aml_ddr_set_filter(struct perf_event *event, u8 axi_id) +{ + struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + int chann; + + if (event->attr.config > ALL_CHAN_COUNTER_ID && + event->attr.config < COUNTER_MAX_ID) { + chann = event->attr.config - CHAN1_COUNTER_ID; + + aml_ddr_config_axi_id(pmu, axi_id, chann); + } +} + +static inline void ddr_cnt_addition(struct dmc_counter *sum, + struct dmc_counter *add1, + struct dmc_counter *add2) +{ + int i; + u64 cnt1, cnt2; + + sum->all_cnt = add1->all_cnt + add2->all_cnt; + sum->all_req = add1->all_req + add2->all_req; + for (i = 0; i < MAX_CHANNEL_NUM; i++) { + cnt1 = add1->channel_cnt[i]; + cnt2 = add2->channel_cnt[i]; + + sum->channel_cnt[i] = cnt1 + cnt2; + } +} + +static void aml_ddr_perf_event_update(struct perf_event *event) +{ + struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + u64 new_raw_count = 0; + struct dmc_counter dc = {0}, sum_dc = {0}; + int idx; + + spin_lock(&pmu->lock); + /* get the remain counters in register. */ + pmu->info.ops->get_counters(&pmu->info, &dc); + + ddr_cnt_addition(&sum_dc, &pmu->counters, &dc); + spin_unlock(&pmu->lock); + + switch (event->attr.config) { + case ALL_CHAN_COUNTER_ID: + new_raw_count = sum_dc.all_cnt; + break; + case CHAN1_COUNTER_ID: + case CHAN2_COUNTER_ID: + case CHAN3_COUNTER_ID: + case CHAN4_COUNTER_ID: + case CHAN5_COUNTER_ID: + case CHAN6_COUNTER_ID: + case CHAN7_COUNTER_ID: + case CHAN8_COUNTER_ID: + idx = event->attr.config - CHAN1_COUNTER_ID; + new_raw_count = sum_dc.channel_cnt[idx]; + break; + default: + dev_err(pmu->dev, "unsupported counter id\n "); + } + + local64_set(&event->count, new_raw_count); +} + +static int aml_ddr_perf_event_init(struct perf_event *event) +{ + struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + struct perf_event *sibling; + u64 config1 = event->attr.config1; + int i, count = 0; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + if (event->cpu < 0) { + dev_warn(pmu->dev, "Can't provide per-task data!\n"); + return -EOPNOTSUPP; + } + + if (event->group_leader->pmu != event->pmu && + !is_software_event(event->group_leader)) + return -EINVAL; + + if ((event)->group_leader == (event)) + list_for_each_entry((sibling), &(event)->sibling_list, + sibling_list) { + if (sibling->pmu != event->pmu && + !is_software_event(sibling)) + return -EINVAL; + } + + /* check if the number of parameters is too much */ + if (event->attr.config != ALL_CHAN_COUNTER_ID) { + for (i = 0; i < MAX_BITMAP; i++) { + if (config1 & 0x1 && ++count > MAX_AXI_PORTS_OF_CHANNEL) { + dev_warn(pmu->dev, "Too many parameters append the event\n"); + return -EOPNOTSUPP; + } + + config1 >>= 1; + } + } + + event->cpu = pmu->cpu; + hwc->idx = -1; + + return 0; +} + +static void aml_ddr_perf_event_start(struct perf_event *event, int flags) +{ + struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + + local64_set(&hwc->prev_count, 0); + local64_set(&event->count, 0); + hwc->state = 0; + + memset(&pmu->counters, 0, sizeof(pmu->counters)); + dmc_pmu_enable(pmu); +} + +static int aml_ddr_perf_event_add(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + u64 config1 = event->attr.config1; + int i, count = 0; + + hwc->idx = event->attr.config; + hwc->state |= PERF_HES_STOPPED; + + /* + * for individual channel event , check if the number of axi_id + * supported is beyond the max + */ + if (event->attr.config != ALL_CHAN_COUNTER_ID) { + for (i = 0; i < MAX_BITMAP; i++) { + if (config1 & 0x1) { + aml_ddr_set_filter(event, i); + if (++count > MAX_AXI_PORTS_OF_CHANNEL) + break; + } + + config1 >>= 1; + } + } + + if (flags & PERF_EF_START) + aml_ddr_perf_event_start(event, flags); + + return 0; +} + +static void aml_ddr_perf_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); + + if (flags & PERF_EF_UPDATE) + aml_ddr_perf_event_update(event); + + dmc_pmu_disable(pmu); + hwc->state |= PERF_HES_STOPPED; +} + +static void aml_ddr_perf_event_del(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + aml_ddr_perf_event_stop(event, PERF_EF_UPDATE); + hwc->idx = -1; +} + +static ssize_t aml_ddr_perf_cpumask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ddr_pmu *pmu = dev_get_drvdata(dev); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); +} + +static struct device_attribute aml_ddr_perf_cpumask_attr = +__ATTR(cpumask, 0444, aml_ddr_perf_cpumask_show, NULL); + +static struct attribute *aml_ddr_perf_cpumask_attrs[] = { + &aml_ddr_perf_cpumask_attr.attr, + NULL, +}; + +static const struct attribute_group ddr_perf_cpumask_attr_group = { + .attrs = aml_ddr_perf_cpumask_attrs, +}; + +static ssize_t +pmu_event_show(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct perf_pmu_events_attr *pmu_attr; + + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +} + +static ssize_t +event_show_unit(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct perf_pmu_events_attr *pmu_attr; + + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + return sprintf(page, "MB\n"); +} + +static ssize_t +event_show_scale(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct perf_pmu_events_attr *pmu_attr; + + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + + /* one count = 16byte = 1.52587890625e-05 MB */ + return sprintf(page, "1.52587890625e-05\n"); +} + +#define AML_DDR_PMU_EVENT_ATTR(_name, _id) \ +{ \ + .attr = __ATTR(_name, 0444, pmu_event_show, NULL), \ + .id = _id, \ +} + +#define AML_DDR_PMU_EVENT_UNIT_ATTR(_name) \ + __ATTR(_name.unit, 0444, event_show_unit, NULL) + +#define AML_DDR_PMU_EVENT_SCALE_ATTR(_name) \ + __ATTR(_name.scale, 0444, event_show_scale, NULL) + +static struct device_attribute event_unit_attrs[] = { + AML_DDR_PMU_EVENT_UNIT_ATTR(total_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_1_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_2_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_3_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_4_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_5_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_6_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_7_rw_bytes), + AML_DDR_PMU_EVENT_UNIT_ATTR(chan_8_rw_bytes), +}; + +static struct device_attribute event_scale_attrs[] = { + AML_DDR_PMU_EVENT_SCALE_ATTR(total_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_1_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_2_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_3_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_4_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_5_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_6_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_7_rw_bytes), + AML_DDR_PMU_EVENT_SCALE_ATTR(chan_8_rw_bytes), +}; + +static struct perf_pmu_events_attr event_attrs[] = { + AML_DDR_PMU_EVENT_ATTR(total_rw_bytes, ALL_CHAN_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_1_rw_bytes, CHAN1_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_2_rw_bytes, CHAN2_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_3_rw_bytes, CHAN3_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_4_rw_bytes, CHAN4_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_5_rw_bytes, CHAN5_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_6_rw_bytes, CHAN6_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_7_rw_bytes, CHAN7_COUNTER_ID), + AML_DDR_PMU_EVENT_ATTR(chan_8_rw_bytes, CHAN8_COUNTER_ID), +}; + +/* three attrs are combined an event */ +static struct attribute *ddr_perf_events_attrs[COUNTER_MAX_ID * 3]; + +static struct attribute_group ddr_perf_events_attr_group = { + .name = "events", + .attrs = ddr_perf_events_attrs, +}; + +/* an attr represents axi id, the max id is less than 256 */ +static struct attribute *ddr_perf_format_attrs[256]; + +static struct attribute_group ddr_perf_format_attr_group = { + .name = "format", + .attrs = ddr_perf_format_attrs, +}; + +static ssize_t aml_ddr_perf_identifier_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct ddr_pmu *pmu = dev_get_drvdata(dev); + + return sprintf(page, "%s\n", pmu->name); +} + +static umode_t aml_ddr_perf_identifier_attr_visible(struct kobject *kobj, + struct attribute *attr, + int n) +{ + return attr->mode; +} + +static struct device_attribute aml_ddr_perf_identifier_attr = +__ATTR(identifier, 0444, aml_ddr_perf_identifier_show, NULL); + +static struct attribute *aml_ddr_perf_identifier_attrs[] = { + &aml_ddr_perf_identifier_attr.attr, + NULL, +}; + +static const struct attribute_group ddr_perf_identifier_attr_group = { + .attrs = aml_ddr_perf_identifier_attrs, + .is_visible = aml_ddr_perf_identifier_attr_visible, +}; + +static const struct attribute_group *attr_groups[] = { + &ddr_perf_events_attr_group, + &ddr_perf_format_attr_group, + &ddr_perf_cpumask_attr_group, + &ddr_perf_identifier_attr_group, + NULL, +}; + +static irqreturn_t dmc_irq_handler(int irq, void *dev_id) +{ + struct dmc_hw_info *info = dev_id; + struct ddr_pmu *pmu; + struct dmc_counter counters; + + if (irq != info->irq_num) + return IRQ_HANDLED; + + pmu = hw_info_to_pmu(info); + WARN_ON(!info->ops->irq_handler); + + if (pmu->pmu_enabled) { + spin_lock(&pmu->lock); + if (info->ops->irq_handler(info, &counters) != 0) { + spin_unlock(&pmu->lock); + goto out; + } + ddr_cnt_accumulate(pmu, &counters); + + /* + * the timer interrupt only supprt + * one shot mode, we have to re-enable + * it in ISR to support continue mode. + */ + info->ops->enable(info); + spin_unlock(&pmu->lock); + } + + dev_dbg(pmu->dev, "counts: %llu %llu %llu, %llu, %llu, %llu\t\t" + "sum: %llu %llu %llu, %llu, %llu, %llu\n", + counters.all_req, + counters.all_cnt, + counters.channel_cnt[0], + counters.channel_cnt[1], + counters.channel_cnt[2], + counters.channel_cnt[3], + + pmu->counters.all_req, + pmu->counters.all_cnt, + pmu->counters.channel_cnt[0], + pmu->counters.channel_cnt[1], + pmu->counters.channel_cnt[2], + pmu->counters.channel_cnt[3]); +out: + return IRQ_HANDLED; +} + +static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct ddr_pmu *pmu = hlist_entry_safe(node, struct ddr_pmu, node); + int target; + + if (cpu != pmu->cpu) + return 0; + + target = cpumask_any_but(cpu_online_mask, cpu); + if (target >= nr_cpu_ids) + return 0; + + perf_pmu_migrate_context(&pmu->pmu, cpu, target); + pmu->cpu = target; + + WARN_ON(irq_set_affinity_hint(pmu->info.irq_num, cpumask_of(pmu->cpu))); + + return 0; +} + +static int ddr_pmu_get_hw_info(struct platform_device *pdev, + struct dmc_hw_info *info) +{ + int ret = -EINVAL; +#ifdef CONFIG_OF + struct device_node *node = pdev->dev.of_node; + /*struct pinctrl *p;*/ + struct resource *res; + resource_size_t *base; + const char *irq_name; + u32 dmc_nr, chann_nr; + int i; + + /* for multiple ddr controller */ + if (of_property_read_u32(node, "dmc_nr", &dmc_nr)) + info->dmc_nr = 1; + + else + info->dmc_nr = dmc_nr; + + for (i = 0; i < dmc_nr; i++) { + /* resource 0 for ddr register base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(&pdev->dev, "can't get ddr reg base\n"); + return -EINVAL; + } + base = ioremap(res->start, res->end - res->start); + if (!base) { + dev_err(&pdev->dev, "couldn't ioremap ddr reg\n"); + return -EINVAL; + } + info->ddr_reg[i] = (void *)base; + } + + /* get DMC monitor channel number */ + if (of_property_read_u32(node, "chann_nr", &chann_nr)) + info->chann_nr = 4; /* g12 serials support 4 channels */ + else + info->chann_nr = chann_nr; + + /* resource i for pll register base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(&pdev->dev, "can't get ddr reg base\n"); + goto err1; + } + base = ioremap(res->start, res->end - res->start); + if (!base) { + dev_err(&pdev->dev, "couldn't ioremap for pll reg\n"); + goto err1; + } + info->pll_reg = (void *)base; + + info->irq_num = of_irq_get(node, 0); + if (info->irq_num < 0) { + dev_err(&pdev->dev, "couldn't get irq\n"); + goto err2; + } + + irq_name = of_get_property(node, "interrupt-names", NULL); + if (!irq_name) + irq_name = "ddr_pmu"; + + ret = request_irq(info->irq_num, dmc_irq_handler, + IRQF_SHARED, irq_name, (void *)info); + if (ret < 0) { + dev_err(&pdev->dev, "ddr request irq failed\n"); + goto err2; + } + + return 0; + +err2: + iounmap(info->pll_reg); +err1: + for (i = 0; i < info->dmc_nr; i++) + iounmap(info->ddr_reg[i]); +#endif + return ret; +} + +static void ddr_pmu_free_hw_info(struct dmc_hw_info *info) +{ + int i; + + iounmap(info->pll_reg); + + for (i = 0; i < info->dmc_nr; i++) + iounmap(info->ddr_reg[i]); +} + +static void fill_event_attr(struct ddr_pmu *pmu) +{ + int i, j, k; + struct attribute **dst = ddr_perf_events_attrs; + + j = 0; + k = 0; + + /* fill ALL_CHAN_COUNTER_ID event */ + dst[j++] = &event_attrs[k].attr.attr; + dst[j++] = &event_unit_attrs[k].attr; + dst[j++] = &event_scale_attrs[k].attr; + + k++; + + pr_info("pmu->info.chann_nr %d\n", pmu->info.chann_nr); + + /* fill each channel event */ + for (i = 0; i < pmu->info.chann_nr; i++, k++) { + dst[j++] = &event_attrs[k].attr.attr; + dst[j++] = &event_unit_attrs[k].attr; + dst[j++] = &event_scale_attrs[k].attr; + } + + dst[j] = NULL; /* mark end */ +} + +static void fmt_attr_fill(struct attribute **fmt_attr) +{ + int i; + + for (i = 0; fmt_attr[i]; i++) + ddr_perf_format_attrs[i] = fmt_attr[i]; + + ddr_perf_format_attrs[i] = NULL; +} + +int aml_ddr_pmu_create(struct platform_device *pdev, + struct dmc_pmu_hw_ops *ops, + struct attribute **fmt_attr) +{ + int ret; + char *name; + struct ddr_pmu *pmu; + + pmu = devm_kzalloc(&pdev->dev, sizeof(struct ddr_pmu), GFP_KERNEL); + if (!pmu) + return -ENOMEM; + + *pmu = (struct ddr_pmu) { + .pmu = (struct pmu) { + .module = THIS_MODULE, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + .task_ctx_nr = perf_invalid_context, + .attr_groups = attr_groups, + .event_init = aml_ddr_perf_event_init, + .add = aml_ddr_perf_event_add, + .del = aml_ddr_perf_event_del, + .start = aml_ddr_perf_event_start, + .stop = aml_ddr_perf_event_stop, + .read = aml_ddr_perf_event_update, + } + }; + + ret = ddr_pmu_get_hw_info(pdev, &pmu->info); + if (ret < 0) + goto pmu_alloc_err; + + fmt_attr_fill(fmt_attr); + + pmu->info.ops = ops; + + spin_lock_init(&pmu->lock); + + pmu->cpu = raw_smp_processor_id(); + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DDR_PERF_DEV_NAME); + if (!name) { + dev_err(&pdev->dev, "couldn't alloc memory for name\n"); + ret = -ENOMEM; + goto get_dev_name_err; + } + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, name, NULL, + ddr_perf_offline_cpu); + + if (ret < 0) { + dev_err(&pdev->dev, "cpuhp_setup_state_multi failed\n"); + goto cpuhp_state_err; + } + pmu->cpuhp_state = ret; + + /* Register the pmu instance for cpu hotplug */ + ret = cpuhp_state_add_instance_nocalls(pmu->cpuhp_state, &pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); + goto cpuhp_instance_err; + } + + fill_event_attr(pmu); + + ret = perf_pmu_register(&pmu->pmu, name, -1); + if (ret) { + dev_err(&pdev->dev, "perf pmu register failed\n"); + goto pmu_register_err; + } + + pmu->name = name; + pmu->dev = &pdev->dev; + pmu->pmu_enabled = false; + + platform_set_drvdata(pdev, pmu); + + dev_info(&pdev->dev, "ddr perf init ok\n"); + + return 0; + +pmu_register_err: + cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node); +cpuhp_instance_err: + cpuhp_remove_multi_state(pmu->cpuhp_state); +cpuhp_state_err: + devm_kfree(&pdev->dev, name); +get_dev_name_err: + ddr_pmu_free_hw_info(&pmu->info); +pmu_alloc_err: + devm_kfree(&pdev->dev, pmu); + + return ret; +} + +int aml_ddr_pmu_remove(struct platform_device *pdev) +{ + struct ddr_pmu *pmu = platform_get_drvdata(pdev); + + perf_pmu_unregister(&pmu->pmu); + cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node); + cpuhp_remove_multi_state(pmu->cpuhp_state); + devm_kfree(pmu->dev, pmu->name); + free_irq(pmu->info.irq_num, &pmu->info); + ddr_pmu_free_hw_info(&pmu->info); + devm_kfree(&pdev->dev, pmu); + + dev_info(&pdev->dev, "ddr perf finit ok\n"); + + return 0; +} + diff --git a/drivers/perf/amlogic/aml_ddr_pmu_g12.c b/drivers/perf/amlogic/aml_ddr_pmu_g12.c new file mode 100644 index 000000000000..29e351157f49 --- /dev/null +++ b/drivers/perf/amlogic/aml_ddr_pmu_g12.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Amlogic, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PORT_MAJOR 32 +#define DEFAULT_XTAL_FREQ 24000000UL + +#define DMC_QOS_IRQ BIT(30) + +/* DMC bandwidth monitor register address offset */ +#define DMC_MON_G12_CTRL0 (0x20 << 2) +#define DMC_MON_G12_CTRL1 (0x21 << 2) +#define DMC_MON_G12_CTRL2 (0x22 << 2) +#define DMC_MON_G12_CTRL3 (0x23 << 2) +#define DMC_MON_G12_CTRL4 (0x24 << 2) +#define DMC_MON_G12_CTRL5 (0x25 << 2) +#define DMC_MON_G12_CTRL6 (0x26 << 2) +#define DMC_MON_G12_CTRL7 (0x27 << 2) +#define DMC_MON_G12_CTRL8 (0x28 << 2) + +#define DMC_MON_G12_ALL_REQ_CNT (0x29 << 2) +#define DMC_MON_G12_ALL_GRANT_CNT (0x2a << 2) +#define DMC_MON_G12_ONE_GRANT_CNT (0x2b << 2) +#define DMC_MON_G12_SEC_GRANT_CNT (0x2c << 2) +#define DMC_MON_G12_THD_GRANT_CNT (0x2d << 2) +#define DMC_MON_G12_FOR_GRANT_CNT (0x2e << 2) +#define DMC_MON_G12_TIMER (0x2f << 2) + +/* Each bit represent a axi line */ +PMU_FORMAT_ATTR(event, "config:0-7"); +PMU_FORMAT_ATTR(arm, "config1:0"); +PMU_FORMAT_ATTR(gpu, "config1:1"); +PMU_FORMAT_ATTR(pcie, "config1:2"); +PMU_FORMAT_ATTR(hdcp, "config1:3"); +PMU_FORMAT_ATTR(hevc_front, "config1:4"); +PMU_FORMAT_ATTR(usb3_0, "config1:6"); +PMU_FORMAT_ATTR(device, "config1:7"); +PMU_FORMAT_ATTR(hevc_back, "config1:8"); +PMU_FORMAT_ATTR(h265enc, "config1:9"); +PMU_FORMAT_ATTR(vpu_read1, "config1:16"); +PMU_FORMAT_ATTR(vpu_read2, "config1:17"); +PMU_FORMAT_ATTR(vpu_read3, "config1:18"); +PMU_FORMAT_ATTR(vpu_write1, "config1:19"); +PMU_FORMAT_ATTR(vpu_write2, "config1:20"); +PMU_FORMAT_ATTR(vdec, "config1:21"); +PMU_FORMAT_ATTR(hcodec, "config1:22"); +PMU_FORMAT_ATTR(ge2d, "config1:23"); + +PMU_FORMAT_ATTR(spicc1, "config1:32"); +PMU_FORMAT_ATTR(usb0, "config1:33"); +PMU_FORMAT_ATTR(dma, "config1:34"); +PMU_FORMAT_ATTR(arb0, "config1:35"); +PMU_FORMAT_ATTR(sd_emmc_b, "config1:36"); +PMU_FORMAT_ATTR(usb1, "config1:37"); +PMU_FORMAT_ATTR(audio, "config1:38"); +PMU_FORMAT_ATTR(aififo, "config1:39"); +PMU_FORMAT_ATTR(parser, "config1:41"); +PMU_FORMAT_ATTR(ao_cpu, "config1:42"); +PMU_FORMAT_ATTR(sd_emmc_c, "config1:43"); +PMU_FORMAT_ATTR(spicc2, "config1:44"); +PMU_FORMAT_ATTR(ethernet, "config1:45"); +PMU_FORMAT_ATTR(sana, "config1:46"); + +/* for sm1 and g12b */ +PMU_FORMAT_ATTR(nna, "config1:10"); + +/* for g12b only */ +PMU_FORMAT_ATTR(gdc, "config1:11"); +PMU_FORMAT_ATTR(mipi_isp, "config1:12"); +PMU_FORMAT_ATTR(arm1, "config1:13"); +PMU_FORMAT_ATTR(sd_emmc_a, "config1:40"); + +/* 256 is enough for axi id */ +static struct attribute *g12_pmu_format_attrs[256] = { + &format_attr_event.attr, + &format_attr_arm.attr, + &format_attr_gpu.attr, + &format_attr_pcie.attr, + &format_attr_hdcp.attr, + &format_attr_hevc_front.attr, + &format_attr_usb3_0.attr, + &format_attr_device.attr, + &format_attr_hevc_back.attr, + &format_attr_h265enc.attr, + &format_attr_vpu_read1.attr, + &format_attr_vpu_read2.attr, + &format_attr_vpu_read3.attr, + &format_attr_vpu_write1.attr, + &format_attr_vpu_write2.attr, + &format_attr_vdec.attr, + &format_attr_hcodec.attr, + &format_attr_ge2d.attr, + &format_attr_spicc1.attr, + &format_attr_usb0.attr, + &format_attr_dma.attr, + &format_attr_arb0.attr, + &format_attr_sd_emmc_b.attr, + &format_attr_usb1.attr, + &format_attr_audio.attr, + &format_attr_aififo.attr, + &format_attr_parser.attr, + &format_attr_ao_cpu.attr, + &format_attr_sd_emmc_c.attr, + &format_attr_spicc2.attr, + &format_attr_ethernet.attr, + &format_attr_sana.attr, + NULL, +}; + +static void append_attr_list(struct attribute *attr) +{ + int i; + + for (i = 0; g12_pmu_format_attrs[i] && i < 255; i++) + ; + + g12_pmu_format_attrs[i] = attr; + + g12_pmu_format_attrs[i + 1] = NULL; +} + +/* calculate ddr clock */ +static unsigned long dmc_g12_get_freq_quick(struct dmc_hw_info *info) +{ + unsigned int val; + unsigned int n, m, od1; + unsigned int od_div = 0xfff; + unsigned long freq = 0; + + val = readl(info->pll_reg); + val = val & 0xfffff; + switch ((val >> 16) & 7) { + case 0: + od_div = 2; + break; + + case 1: + od_div = 3; + break; + + case 2: + od_div = 4; + break; + + case 3: + od_div = 6; + break; + + case 4: + od_div = 8; + break; + + default: + break; + } + + m = val & 0x1ff; + n = ((val >> 10) & 0x1f); + od1 = (((val >> 19) & 0x1)) == 1 ? 2 : 1; + freq = DEFAULT_XTAL_FREQ / 1000; /* avoid overflow */ + if (n) + freq = ((((freq * m) / n) >> od1) / od_div) * 1000; + + return freq; +} + +#ifdef DEBUG +static void g12_dump_reg(struct dmc_hw_info *db) +{ + int s = 0, i; + unsigned int r; + + for (i = 0; i < 9; i++) { + r = readl(db->ddr_reg[0] + (DMC_MON_G12_CTRL0 + (i << 2))); + pr_notice("DMC_MON_CTRL%d: %08x\n", i, r); + } + r = readl(db->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT); + pr_notice("DMC_MON_ALL_REQ_CNT: %08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT); + pr_notice("DMC_MON_ALL_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_ONE_GRANT_CNT); + pr_notice("DMC_MON_ONE_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_SEC_GRANT_CNT); + pr_notice("DMC_MON_SEC_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_THD_GRANT_CNT); + pr_notice("DMC_MON_THD_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_FOR_GRANT_CNT); + pr_notice("DMC_MON_FOR_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg[0] + DMC_MON_G12_TIMER); + pr_notice("DMC_MON_TIMER: %08x\n", r); +} +#endif + +static void dmc_g12_counter_enable(struct dmc_hw_info *info) +{ + unsigned int val; + unsigned long clock_count = dmc_g12_get_freq_quick(info) / 10; /* 100ms */ + + writel(clock_count, info->ddr_reg[0] + DMC_MON_G12_TIMER); + + val = readl(info->ddr_reg[0] + DMC_MON_G12_CTRL0); + + /* enable all channel */ + val = BIT(31) | /* enable bit */ + BIT(20) | /* use timer */ + 0x0f; /* 4 channels */ + + writel(val, info->ddr_reg[0] + DMC_MON_G12_CTRL0); + +#ifdef DEBUG + g12_dump_reg(info); +#endif +} + +static void dmc_g12_config_fiter(struct dmc_hw_info *info, + int port, int channel) +{ + u32 val; + u32 rp[MAX_CHANNEL_NUM] = {DMC_MON_G12_CTRL1, DMC_MON_G12_CTRL3, + DMC_MON_G12_CTRL5, DMC_MON_G12_CTRL7}; + u32 rs[MAX_CHANNEL_NUM] = {DMC_MON_G12_CTRL2, DMC_MON_G12_CTRL4, + DMC_MON_G12_CTRL6, DMC_MON_G12_CTRL8}; + int subport = -1; + + /* clear all port mask */ + if (port < 0) { + writel(0, info->ddr_reg[0] + rp[channel]); + writel(0, info->ddr_reg[0] + rs[channel]); + return; + } + + if (port >= PORT_MAJOR) + subport = port - PORT_MAJOR; + + if (subport < 0) { + val = readl(info->ddr_reg[0] + rp[channel]); + val |= (1 << port); + writel(val, info->ddr_reg[0] + rp[channel]); + val = 0xffff; + writel(val, info->ddr_reg[0] + rs[channel]); + } else { + val = BIT(23); /* select device */ + writel(val, info->ddr_reg[0] + rp[channel]); + val = readl(info->ddr_reg[0] + rs[channel]); + val |= (1 << subport); + writel(val, info->ddr_reg[0] + rs[channel]); + } +} + +static void dmc_g12_config_axi_id(struct dmc_hw_info *info, int axi_id, int channel) +{ + if (channel > info->chann_nr) + return; + + dmc_g12_config_fiter(info, axi_id, channel); +} + +static void dmc_g12_counter_disable(struct dmc_hw_info *info) +{ + int i; + + /* clear timer */ + writel(0, info->ddr_reg[0] + DMC_MON_G12_CTRL0); + writel(0, info->ddr_reg[0] + DMC_MON_G12_TIMER); + + writel(0, info->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT); + writel(0, info->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT); + writel(0, info->ddr_reg[0] + DMC_MON_G12_ONE_GRANT_CNT); + writel(0, info->ddr_reg[0] + DMC_MON_G12_SEC_GRANT_CNT); + writel(0, info->ddr_reg[0] + DMC_MON_G12_THD_GRANT_CNT); + writel(0, info->ddr_reg[0] + DMC_MON_G12_FOR_GRANT_CNT); + + /* clear port channel mapping */ + for (i = 0; i < info->chann_nr; i++) + dmc_g12_config_fiter(info, -1, i); +} + +static void dmc_g12_get_counters(struct dmc_hw_info *info, + struct dmc_counter *counter) +{ + int i; + unsigned int reg; + + counter->all_cnt = readl(info->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT); + counter->all_req = readl(info->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT); + + for (i = 0; i < info->chann_nr; i++) { + reg = DMC_MON_G12_ONE_GRANT_CNT + (i << 2); + counter->channel_cnt[i] = readl(info->ddr_reg[0] + reg); + } +} + +static int dmc_g12_irq_handler(struct dmc_hw_info *info, + struct dmc_counter *counter) +{ + unsigned int val; + int ret = -1; + + val = readl(info->ddr_reg[0] + DMC_MON_G12_CTRL0); + if (val & DMC_QOS_IRQ) { + dmc_g12_get_counters(info, counter); + /* clear irq flags */ + writel(val, info->ddr_reg[0] + DMC_MON_G12_CTRL0); + ret = 0; + } + return ret; +} + +static struct dmc_pmu_hw_ops g12_ops = { + .enable = dmc_g12_counter_enable, + .disable = dmc_g12_counter_disable, + .irq_handler = dmc_g12_irq_handler, + .get_counters = dmc_g12_get_counters, + .config_axi_id = dmc_g12_config_axi_id, +}; + +static int __init g12_ddr_pmu_probe(struct platform_device *pdev) +{ +#ifdef CONFIG_OF + struct device_node *node = pdev->dev.of_node; + const char *model; + + if (of_property_read_string(node, "model", &model)) + return -EINVAL; + + dev_info(&pdev->dev, "%s", model); + + if (strcmp(model, "g12a") == 0) { + dev_info(&pdev->dev, "ddr pmu for g12a\n"); + } else if (strcmp(model, "g12b") == 0) { + dev_info(&pdev->dev, "ddr pmu for g12b\n"); + + append_attr_list(&format_attr_nna.attr); + append_attr_list(&format_attr_gdc.attr); + append_attr_list(&format_attr_arm1.attr); + append_attr_list(&format_attr_mipi_isp.attr); + append_attr_list(&format_attr_sd_emmc_a.attr); + } else if (strcmp(model, "sm1") == 0) { + dev_info(&pdev->dev, "ddr pmu for sm1\n"); + + append_attr_list(&format_attr_nna.attr); + } +#endif + return aml_ddr_pmu_create(pdev, &g12_ops, g12_pmu_format_attrs); +} + +static int __exit g12_ddr_pmu_remove(struct platform_device *pdev) +{ + aml_ddr_pmu_remove(pdev); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id aml_ddr_pmu_dt_match[] = { + { + .compatible = "amlogic,g12-ddr-pmu", + }, + {} +}; +#endif + +static struct platform_driver g12_ddr_pmu_driver = { + .driver = { + .name = "amlogic,ddr-pmu", + .owner = THIS_MODULE, + #ifdef CONFIG_OF + .of_match_table = aml_ddr_pmu_dt_match, + #endif + }, + .remove = g12_ddr_pmu_remove, +}; + +static int __init aml_g12_ddr_pmu_init(void) +{ + return platform_driver_probe(&g12_ddr_pmu_driver, g12_ddr_pmu_probe); +} + +static void __exit aml_g12_ddr_pmu_exit(void) +{ + platform_driver_unregister(&g12_ddr_pmu_driver); +} + +module_init(aml_g12_ddr_pmu_init); +module_exit(aml_g12_ddr_pmu_exit); +MODULE_LICENSE("GPL"); diff --git a/include/soc/amlogic/aml_ddr_pmu.h b/include/soc/amlogic/aml_ddr_pmu.h new file mode 100644 index 000000000000..4f60a9f4fa4a --- /dev/null +++ b/include/soc/amlogic/aml_ddr_pmu.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 Amlogic, Inc. All rights reserved. + */ + +#ifndef __AML_DDR_PMU_H__ +#define __AML_DDR_PMU_H__ + +#define MAX_CHANNEL_NUM 8 + +enum { + ALL_CHAN_COUNTER_ID, + CHAN1_COUNTER_ID, + CHAN2_COUNTER_ID, + CHAN3_COUNTER_ID, + CHAN4_COUNTER_ID, + CHAN5_COUNTER_ID, + CHAN6_COUNTER_ID, + CHAN7_COUNTER_ID, + CHAN8_COUNTER_ID, + COUNTER_MAX_ID, +}; + +struct dmc_hw_info { + struct dmc_pmu_hw_ops *ops; + void __iomem *ddr_reg[4]; + unsigned long timer_value; /* Timer value in TIMER register */ + void __iomem *pll_reg; + int irq_num; /* irq vector number */ + int dmc_nr; /* The number of dmc controller */ + int chann_nr; /* The number of dmc bandwidth monitor channels */ + int id; /* The number of supported channels */ +}; + +struct dmc_counter { + u64 all_cnt; /* The count of all requests come in/out ddr controller */ + union { + u64 all_req; + struct { + u64 all_idle_cnt; + u64 all_16bit_cnt; + }; + }; + u64 channel_cnt[MAX_CHANNEL_NUM]; /* To save a DMC bandwidth-monitor channel counter */ +}; + +struct dmc_pmu_hw_ops { + void (*enable)(struct dmc_hw_info *info); + void (*disable)(struct dmc_hw_info *info); + /* Bind an axi line to a bandwidth-monitor channel */ + void (*config_axi_id)(struct dmc_hw_info *info, int axi_id, int chann); + int (*irq_handler)(struct dmc_hw_info *info, + struct dmc_counter *counter); + void (*get_counters)(struct dmc_hw_info *info, + struct dmc_counter *counter); +}; + +int aml_ddr_pmu_create(struct platform_device *pdev, + struct dmc_pmu_hw_ops *ops, + struct attribute **fmt_attr); +int aml_ddr_pmu_remove(struct platform_device *pdev); + +#endif /* __AML_DDR_PMU_H__ */ From patchwork Tue Jul 12 06:36:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiucheng Xu X-Patchwork-Id: 12914536 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 47261C433EF for ; Tue, 12 Jul 2022 06:37:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=YnfBeW0EZguZJqYqHlGM4onb8lq4JcNzw8cH51jxwto=; b=btaOfrFnQXPcnW 689qJuZ3n3BTDLq1JX7dnW9H3FA/P5RE5oMM6cAfB5xw0xouNuGULkz4CbMHDwbvmnA1qVgZ4ycgs gFfTyt/RQbG2koqlLtNthVFWtVxNu4JKk2qMwFAMz8bf7sTa7a+trcVurFBWR6ns8enJUhEZTk4mf i0jZlHwK1EPNxyU8iwGabLyDCLLZwRx2tuLNAe18JSGDe5GOogKLYmPl9dv1Oft5cWM0+/7IQ+keo 4miWScTxjcV7/H0IzXBlOUnjN9SSvztkRnDkR8xExX+GWOkqMmYl2ajbdpX5Ag2a43vybrJ4A/W8U YxgIxGB+GznhJ9WOr56A==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9WW-0083Nn-Jw; Tue, 12 Jul 2022 06:37:32 +0000 Received: from mail-sh.amlogic.com ([58.32.228.43]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9WD-0083Fb-IY; Tue, 12 Jul 2022 06:37:15 +0000 Received: from droid01-xa.amlogic.com (10.88.11.200) by mail-sh.amlogic.com (10.18.11.5) with Microsoft SMTP Server id 15.1.2176.14; Tue, 12 Jul 2022 14:36:58 +0800 From: Jiucheng Xu To: , , , CC: Rob Herring , Krzysztof Kozlowski , Will Deacon , Mark Rutland , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl , Chris Healy , Jiucheng Xu Subject: [PATCH 2/4] docs/perf: Add documentation for the Amlogic G12 DDR PMU Date: Tue, 12 Jul 2022 14:36:39 +0800 Message-ID: <20220712063641.2790997-2-jiucheng.xu@amlogic.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> References: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> MIME-Version: 1.0 X-Originating-IP: [10.88.11.200] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220711_233714_282179_1B5577D5 X-CRM114-Status: GOOD ( 17.05 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org Add a user guide to show how to use DDR PMU to monitor DDR bandwidth on Amlogic G12 SoC Signed-off-by: Jiucheng Xu --- .../admin-guide/perf/aml-ddr-pmu.rst | 70 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 71 insertions(+) create mode 100644 Documentation/admin-guide/perf/aml-ddr-pmu.rst diff --git a/Documentation/admin-guide/perf/aml-ddr-pmu.rst b/Documentation/admin-guide/perf/aml-ddr-pmu.rst new file mode 100644 index 000000000000..a441eeb7d968 --- /dev/null +++ b/Documentation/admin-guide/perf/aml-ddr-pmu.rst @@ -0,0 +1,70 @@ +.. SPDX-License-Identifier: GPL-2.0 +=========================================================== +Amlogic SoC DDR Bandwidth Performance Monitoring Unit (PMU) +=========================================================== + +There is a bandwidth monitor inside the DRAM contorller. The monitor include +4 channels which can count the read/write request of accessing DRAM individually. +It can be helpful to show if the performance bottleneck is on DDR bandwidth. + +Currently, this driver supports the following 5 Perf events: + +aml_ddr_bw/total_rw_bytes/ +aml_ddr_bw/chan_1_rw_bytes/ +aml_ddr_bw/chan_2_rw_bytes/ +aml_ddr_bw/chan_3_rw_bytes/ +aml_ddr_bw/chan_4_rw_bytes/ + +aml_ddr_bw/chan_{1,2,3,4}_rw_bytes/ events are the channel related events. +Each channel support using keywords as filter, which can let the channel +to monitor the individual IP module in SoC. + +The following keywords are the filter: + +arm - DDR access request from CPU +vpu_read1 - DDR access request from OSD + VPP read +gpu - DDR access request from 3D GPU +pcie - DDR access request from PCIe controller +hdcp - DDR access request from HDCP controller +hevc_front - DDR access request from HEVC codec front end +usb3_0 - DDR access request from USB3.0 controller +hevc_back - DDR access request from HEVC codec back end +h265enc - DDR access request from HEVC encoder +vpu_read2 - DDR access request from DI read +vpu_write1 - DDR access request from VDIN write +vpu_write2 - DDR access request from di write +vdec - DDR access request from legacy codec video decoder +hcodec - DDR access request from H264 encoder +ge2d - DDR access request from ge2d +spicc1 - DDR access request from SPI controller 1 +usb0 - DDR access request from USB2.0 controller 0 +dma - DDR access request from system DMA controller 1 +arb0 - DDR access request from arb0 +sd_emmc_b - DDR access request from SD eMMC b controller +usb1 - DDR access request from USB2.0 controller 1 +audio - DDR access request from Audio module +sd_emmc_c - DDR access request from SD eMMC c controller +spicc2 - DDR access request from SPI controller 2 +ethernet - DDR access request from Ethernet controller + +The following command is to show the total DDR bandwidth: + + .. code-block::bash + + perf stat -a -e aml_ddr_bw/total_rw_bytes/ -I 1000 sleep 10 + +This command will print the total DDR bandwidth per second. + +The following commands are to show how to use filter parameters: + + .. code-block::bash + + perf stat -a -e aml_ddr_bw/chan_1_rw_bytes,arm=1/ -I 1000 sleep 10 + perf stat -a -e aml_ddr_bw/chan_2_rw_bytes,gpu=1/ -I 1000 sleep 10 + perf stat -a -e aml_ddr_bw/chan_3_rw_bytes,arm=1,gpu=1/ -I 1000 sleep 10 + +The 1st command show how to use channel 1 to monitor the DDR bandwidth from ARM. +The 2nd command show using channel 2 to get the DDR bandwidth of GPU. +The 3rd command show using channel 3 to monitor the sum of ARM and GPU. + + diff --git a/MAINTAINERS b/MAINTAINERS index cb6ee59a4f44..fd2a56a339b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1054,6 +1054,7 @@ AMLOGIC DDR PMU DRIVER M: Jiucheng Xu S: Supported W: http://www.amlogic.com +F: Documentation/admin-guide/perf/aml-ddr-pmu.rst F: drivers/perf/amlogic/ F: include/soc/amlogic/ From patchwork Tue Jul 12 06:36:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiucheng Xu X-Patchwork-Id: 12914537 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 12D49C43334 for ; Tue, 12 Jul 2022 06:37:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=mKC4ktKSTLIQlJvk3QT6VTKwhtJboT/qxDksdDXCH1Y=; b=RnHol8K2vajuso wi/zHSw0b2eYEC587mao0uYKZCmfkaYC300cPe1m2tZ9Hl5v5nbT9OVXIFMCyXwDn0e0RhJIZ1K7C kyqhJO1VnsxP9GTh2NYDo0pXbfiDyYCIgOwxZc8GgbFOaPDKElg466PUYtK4fKPm1wrxUT84bpIrQ atHJRjOyu91xtQdO/8N9Vq2ozaQeSZQA6DY5/I680rM/8dRjtms0EtC0lhT8oS+CU5OC56vafousa F2X5EElloIuo98lx5wUxD+7ZFAkrEa7KyLF+T5PfttP1Xa5U7E+2Cgrpjiq3LhOGkKptMQO0fE4Se Lg//3qmgGrgZoytTn5+A==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9Wf-0083Qo-C7; Tue, 12 Jul 2022 06:37:41 +0000 Received: from mail-sh.amlogic.com ([58.32.228.43]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9WG-0083Fb-I9; Tue, 12 Jul 2022 06:37:18 +0000 Received: from droid01-xa.amlogic.com (10.88.11.200) by mail-sh.amlogic.com (10.18.11.5) with Microsoft SMTP Server id 15.1.2176.14; Tue, 12 Jul 2022 14:36:59 +0800 From: Jiucheng Xu To: , , , CC: Rob Herring , Krzysztof Kozlowski , Will Deacon , Mark Rutland , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl , Chris Healy , Jiucheng Xu Subject: [PATCH 3/4] arm64:dts:meson: Add DDR PMU node Date: Tue, 12 Jul 2022 14:36:40 +0800 Message-ID: <20220712063641.2790997-3-jiucheng.xu@amlogic.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> References: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> MIME-Version: 1.0 X-Originating-IP: [10.88.11.200] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220711_233716_908902_53E3316D X-CRM114-Status: UNSURE ( 8.95 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org Add DDR PMU device node for G12 series SoC Signed-off-by: Jiucheng Xu --- arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 11 +++++++++++ arch/arm64/boot/dts/amlogic/meson-g12a.dtsi | 4 ++++ arch/arm64/boot/dts/amlogic/meson-g12b.dtsi | 4 ++++ arch/arm64/boot/dts/amlogic/meson-sm1.dtsi | 4 ++++ 4 files changed, 23 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi index 45947c1031c4..dc9da2f29b30 100644 --- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi @@ -2146,6 +2146,17 @@ hdmi_tx_out: endpoint { }; }; + ddr_pmu: ddr_pmu { + compatible = "amlogic,g12-ddr-pmu"; + status = "okay"; + dmc_nr = <1>; + chann_nr = <4>; + reg = <0x0 0xff638000 0x0 0x100 + 0x0 0xff638c00 0x0 0x100>; + interrupts = ; + interrupt-names = "ddr_pmu"; + }; + gic: interrupt-controller@ffc01000 { compatible = "arm,gic-400"; reg = <0x0 0xffc01000 0 0x1000>, diff --git a/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi index fb0ab27d1f64..78bd33fb5553 100644 --- a/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi @@ -133,3 +133,7 @@ map1 { }; }; }; + +&ddr_pmu { + model = "g12a"; +}; diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi index ee8fcae9f9f0..3f4df1f58f95 100644 --- a/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi @@ -139,3 +139,7 @@ map1 { &mali { dma-coherent; }; + +&ddr_pmu { + model = "g12b"; +}; diff --git a/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi b/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi index 80737731af3f..a0c15fd0ce9b 100644 --- a/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi @@ -543,3 +543,7 @@ &vpu { &usb { power-domains = <&pwrc PWRC_SM1_USB_ID>; }; + +&ddr_pmu { + model = "sm1"; +}; From patchwork Tue Jul 12 06:36:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiucheng Xu X-Patchwork-Id: 12914538 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 14DC0C433EF for ; Tue, 12 Jul 2022 06:38:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=rqQ/UQDCILGObTa1dhF1mev0w7Mb0Az2NEt3zjMiCXw=; b=BDh7ls5BGwM1IW N/z7Gp9ckPDUb+bSsnGQqxx/MinkUAqT+yqNWqyvt23/j2cIO78yWSYwLv+uRS9wKC7NLcr8ejejQ TiCLVLIMpL7KiimHjZbvQ4k3vLttc6At64H48Fwt1po86zH6fjs6zCXIbMKY1jbvWkO9FDBUBQOXo DdNFYvr29DDbC/ZIF9JjZV8U4Ktwt3N0S9MRVvaWbDHRZmZM5xh0F8CrmChOyqElvzDYgBCcLiX5q 0Rsyir4+JjhTYG9MP+7unIt97Nuar4KNP0blk+i5lUi2FR7Qbgy1+dSlUXg2/sQwldTQFi4TuT1Ew p45mHsdAlyrRGTiGB7sQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9Wp-0083Vx-Ei; Tue, 12 Jul 2022 06:37:51 +0000 Received: from mail-sh.amlogic.com ([58.32.228.43]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oB9WI-0083Fb-Lq; Tue, 12 Jul 2022 06:37:20 +0000 Received: from droid01-xa.amlogic.com (10.88.11.200) by mail-sh.amlogic.com (10.18.11.5) with Microsoft SMTP Server id 15.1.2176.14; Tue, 12 Jul 2022 14:37:00 +0800 From: Jiucheng Xu To: , , , CC: Rob Herring , Krzysztof Kozlowski , Will Deacon , Mark Rutland , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl , Chris Healy , Jiucheng Xu Subject: [PATCH 4/4] dt-binding:perf: Add Amlogic DDR PMU Date: Tue, 12 Jul 2022 14:36:41 +0800 Message-ID: <20220712063641.2790997-4-jiucheng.xu@amlogic.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> References: <20220712063641.2790997-1-jiucheng.xu@amlogic.com> MIME-Version: 1.0 X-Originating-IP: [10.88.11.200] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220711_233718_731279_21BB190E X-CRM114-Status: GOOD ( 12.90 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org Add binding documentation for the Amlogic G12 series DDR performance monitor unit. Signed-off-by: Jiucheng Xu --- .../devicetree/bindings/perf/aml-ddr-pmu.yaml | 51 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/perf/aml-ddr-pmu.yaml diff --git a/Documentation/devicetree/bindings/perf/aml-ddr-pmu.yaml b/Documentation/devicetree/bindings/perf/aml-ddr-pmu.yaml new file mode 100644 index 000000000000..c586b4ab4009 --- /dev/null +++ b/Documentation/devicetree/bindings/perf/aml-ddr-pmu.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/perf/aml-ddr-pmu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic G12 DDR performance monitor + +maintainers: + - Jiucheng Xu + +properties: + compatible: + oneOf: + - enum: + - aml,g12-ddr-pmu + - items: + - enum: + - aml,g12-ddr-pmu + - const: aml,g12-ddr-pmu + + reg: + maxItems: 2 + + interrupts: + maxItems: 1 + +required: + - compatible + - model + - dmc_nr + - chann_nr + - reg + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + ddr_pmu: ddr_pmu { + compatible = "amlogic,g12-ddr-pmu"; + model = "g12a"; + dmc_nr = <1>; + chann_nr = <4>; + reg = <0x0 0xff638000 0x0 0x100 + 0x0 0xff638c00 0x0 0x100>; + interrupts = ; + interrupt-names = "ddr_pmu"; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index fd2a56a339b4..ac0a1df4622d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1055,6 +1055,7 @@ M: Jiucheng Xu S: Supported W: http://www.amlogic.com F: Documentation/admin-guide/perf/aml-ddr-pmu.rst +F: Documentation/devicetree/bindings/perf/aml-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/