Merge branch 'mr-origin-149'

parents ab514171 7a6ee989
......@@ -49,6 +49,10 @@ properties:
operating-points-v2: true
opp-table: true
fsl,ddr-pmu:
description: Phandle of PMU node
$ref: "/schemas/types.yaml#/definitions/phandle"
required:
- reg
- compatible
......@@ -60,6 +64,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
ddrc: memory-controller@3d400000 {
compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc";
reg = <0x3d400000 0x400000>;
......@@ -70,3 +75,10 @@ examples:
<&clk IMX8MM_CLK_DRAM_APB>;
operating-points-v2 = <&ddrc_opp_table>;
};
ddr_pmu: ddr-pmu@3d800000 {
compatible = "fsl,imx8mm-ddr-pmu", "fsl,imx8m-ddr-pmu";
reg = <0x3d800000 0x400000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
};
......@@ -970,9 +970,10 @@ ddrc: memory-controller@3d400000 {
<&clk IMX8MM_DRAM_PLL>,
<&clk IMX8MM_CLK_DRAM_ALT>,
<&clk IMX8MM_CLK_DRAM_APB>;
fsl,ddr-pmu = <&ddr_pmu>;
};
ddr-pmu@3d800000 {
ddr_pmu: ddr-pmu@3d800000 {
compatible = "fsl,imx8mm-ddr-pmu", "fsl,imx8m-ddr-pmu";
reg = <0x3d800000 0x400000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
......
......@@ -837,9 +837,10 @@ ddrc: memory-controller@3d400000 {
<&clk IMX8MN_DRAM_PLL>,
<&clk IMX8MN_CLK_DRAM_ALT>,
<&clk IMX8MN_CLK_DRAM_APB>;
fsl,ddr-pmu = <&ddr_pmu>;
};
ddr-pmu@3d800000 {
ddr_pmu: ddr-pmu@3d800000 {
compatible = "fsl,imx8mn-ddr-pmu", "fsl,imx8m-ddr-pmu";
reg = <0x3d800000 0x400000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
......
......@@ -1511,9 +1511,10 @@ ddrc: memory-controller@3d400000 {
<&clk IMX8MQ_DRAM_PLL_OUT>,
<&clk IMX8MQ_CLK_DRAM_ALT>,
<&clk IMX8MQ_CLK_DRAM_APB>;
fsl,ddr-pmu = <&ddr_pmu>;
};
ddr-pmu@3d800000 {
ddr_pmu: ddr-pmu@3d800000 {
compatible = "fsl,imx8mq-ddr-pmu", "fsl,imx8m-ddr-pmu";
reg = <0x3d800000 0x400000>;
interrupt-parent = <&gic>;
......
......@@ -557,7 +557,7 @@ CONFIG_DRM_MXSFB=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_PWM=y
CONFIG_BACKLIGHT_LED=y
CONFIG_BACKLIGHT_LED=m
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
......@@ -872,6 +872,7 @@ CONFIG_RPMSG_VIRTIO=y
CONFIG_PM_DEVFREQ=y
CONFIG_DEVFREQ_GOV_PERFORMANCE=y
CONFIG_DEVFREQ_GOV_POWERSAVE=y
CONFIG_DEVFREQ_GOV_PASSIVE=y
CONFIG_ARM_IMX_BUS_DEVFREQ=y
CONFIG_ARM_IMX8M_DDRC_DEVFREQ=y
CONFIG_PM_DEVFREQ_EVENT=y
......
......@@ -983,6 +983,32 @@ struct devfreq *devm_devfreq_add_device(struct device *dev,
EXPORT_SYMBOL(devm_devfreq_add_device);
#ifdef CONFIG_OF
/*
* devfreq_get_devfreq_by_node - Get the devfreq device from devicetree
* @node - pointer to device_node
*
* return the instance of devfreq device
*/
struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node)
{
struct devfreq *devfreq;
if (!node)
return ERR_PTR(-EINVAL);
mutex_lock(&devfreq_list_lock);
list_for_each_entry(devfreq, &devfreq_list, node) {
if (devfreq->dev.parent
&& devfreq->dev.parent->of_node == node) {
mutex_unlock(&devfreq_list_lock);
return devfreq;
}
}
mutex_unlock(&devfreq_list_lock);
return ERR_PTR(-ENODEV);
}
/*
* devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree
* @dev - instance to the given device
......@@ -1005,26 +1031,24 @@ struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index)
if (!node)
return ERR_PTR(-ENODEV);
mutex_lock(&devfreq_list_lock);
list_for_each_entry(devfreq, &devfreq_list, node) {
if (devfreq->dev.parent
&& devfreq->dev.parent->of_node == node) {
mutex_unlock(&devfreq_list_lock);
of_node_put(node);
return devfreq;
}
}
mutex_unlock(&devfreq_list_lock);
devfreq = devfreq_get_devfreq_by_node(node);
of_node_put(node);
return ERR_PTR(-EPROBE_DEFER);
return devfreq;
}
#else
struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node)
{
return ERR_PTR(-ENODEV);
}
struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index)
{
return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_OF */
EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_node);
EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle);
/**
......
......@@ -16,6 +16,7 @@ struct imx_bus {
struct devfreq_dev_profile profile;
struct devfreq *devfreq;
struct clk *clk;
struct devfreq_passive_data passive_data;
struct platform_device *icc_pdev;
};
......@@ -100,6 +101,8 @@ static int imx_bus_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct imx_bus *priv;
const char *gov = DEVFREQ_GOV_USERSPACE;
struct device_node *passive_parent_node;
void *gov_data = NULL;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
......@@ -136,8 +139,33 @@ static int imx_bus_probe(struct platform_device *pdev)
priv->profile.get_cur_freq = imx_bus_get_cur_freq;
priv->profile.initial_freq = clk_get_rate(priv->clk);
/* Handle passive devfreq parent link */
passive_parent_node = of_parse_phandle(dev->of_node, "fsl,ddrc", 0);
if (passive_parent_node) {
dev_info(dev, "passive parent node %pOF\n", passive_parent_node);
priv->passive_data.parent = devfreq_get_devfreq_by_node(
passive_parent_node);
of_node_put(passive_parent_node);
if (!IS_ERR(priv->passive_data.parent)) {
dev_info(dev, "passive parent device %s\n",
dev_name(priv->passive_data.parent->dev.parent));
gov = DEVFREQ_GOV_PASSIVE;
gov_data = &priv->passive_data;
} else if (priv->passive_data.parent != ERR_PTR(-ENODEV)) {
// -ENODEV means no parent: not an error.
ret = PTR_ERR(priv->passive_data.parent);
dev_warn(dev, "failed to init passive parent: %d\n", ret);
goto err;
} else {
ret = -EPROBE_DEFER;
goto err;
}
}
priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
gov, NULL);
gov, gov_data);
if (IS_ERR(priv->devfreq)) {
ret = PTR_ERR(priv->devfreq);
dev_err(dev, "failed to add devfreq device: %d\n", ret);
......
......@@ -13,6 +13,9 @@
#include <linux/clk-provider.h>
#include <linux/arm-smccc.h>
#include <asm/perf_event.h>
#include <linux/perf_event.h>
#define IMX_SIP_DDR_DVFS 0xc2000004
/* Query available frequencies. */
......@@ -34,6 +37,9 @@ struct imx8m_ddrc_freq {
/* Hardware limitation */
#define IMX8M_DDRC_MAX_FREQ_COUNT 4
/* Percentage ratio between PMU events and DDRC frequency */
#define IMX8M_DDRC_SATURATION_RATIO 20
/*
* i.MX8M DRAM Controller clocks have the following structure (abridged):
*
......@@ -74,6 +80,17 @@ struct imx8m_ddrc {
int freq_count;
struct imx8m_ddrc_freq freq_table[IMX8M_DDRC_MAX_FREQ_COUNT];
/* For measuring load with perf events: */
struct pmu *pmu;
struct perf_event_attr rd_event_attr;
struct perf_event_attr wr_event_attr;
struct perf_event *rd_event;
struct perf_event *wr_event;
u64 last_rd_val, last_rd_ena, last_rd_run;
u64 last_wr_val, last_wr_ena, last_wr_run;
};
static struct imx8m_ddrc_freq *imx8m_ddrc_find_freq(struct imx8m_ddrc *priv,
......@@ -285,13 +302,106 @@ static int imx8m_ddrc_get_dev_status(struct device *dev,
{
struct imx8m_ddrc *priv = dev_get_drvdata(dev);
stat->busy_time = 0;
stat->total_time = 0;
stat->current_frequency = clk_get_rate(priv->dram_core);
if (priv->rd_event && priv->wr_event) {
u64 rd_delta, rd_val, rd_ena, rd_run;
u64 wr_delta, wr_val, wr_ena, wr_run;
rd_val = perf_event_read_value(priv->rd_event,
&rd_ena, &rd_run);
wr_val = perf_event_read_value(priv->wr_event,
&wr_ena, &wr_run);
rd_delta = (rd_val - priv->last_rd_val) *
(rd_ena - priv->last_rd_ena);
do_div(rd_delta, rd_run - priv->last_rd_run);
priv->last_rd_val = rd_val;
priv->last_rd_ena = rd_ena;
priv->last_rd_run = rd_run;
wr_delta = (wr_val - priv->last_wr_val) *
(wr_ena - priv->last_wr_ena);
do_div(wr_delta, wr_run - priv->last_wr_run);
priv->last_wr_val = wr_val;
priv->last_wr_ena = wr_ena;
priv->last_wr_run = wr_run;
stat->busy_time = 100 * (rd_delta + wr_delta) /
IMX8M_DDRC_SATURATION_RATIO;
stat->total_time = stat->current_frequency;
} else {
stat->busy_time = 0;
stat->total_time = 0;
}
return 0;
}
static int imx8m_ddrc_perf_disable(struct imx8m_ddrc *priv)
{
/* release and set to NULL */
if (!IS_ERR_OR_NULL(priv->rd_event))
perf_event_release_kernel(priv->rd_event);
if (!IS_ERR_OR_NULL(priv->wr_event))
perf_event_release_kernel(priv->wr_event);
priv->rd_event = NULL;
priv->wr_event = NULL;
return 0;
}
static int imx8m_ddrc_perf_enable(struct imx8m_ddrc *priv)
{
int ret;
priv->rd_event_attr.size = sizeof(priv->rd_event_attr);
priv->rd_event_attr.type = priv->pmu->type;
// perf_dfi_rd_data_cycles in reference manual
priv->rd_event_attr.config = 0x2a;
priv->rd_event = perf_event_create_kernel_counter(
&priv->rd_event_attr, 0, NULL, NULL, NULL);
if (IS_ERR(priv->rd_event)) {
ret = PTR_ERR(priv->rd_event);
goto err;
}
priv->wr_event_attr.size = sizeof(priv->wr_event_attr);
priv->wr_event_attr.type = priv->pmu->type;
// perf_dfi_wr_data_cycles in reference manual
priv->wr_event_attr.config = 0x2b;
priv->wr_event = perf_event_create_kernel_counter(
&priv->wr_event_attr, 0, NULL, NULL, NULL);
if (IS_ERR(priv->wr_event)) {
ret = PTR_ERR(priv->wr_event);
goto err;
}
return 0;
err:
imx8m_ddrc_perf_disable(priv);
return ret;
}
static int imx8m_ddrc_init_events(struct device *dev,
struct device_node *events_node)
{
struct imx8m_ddrc *priv = dev_get_drvdata(dev);
priv->pmu = perf_get_pmu_by_node(events_node);
if (!priv->pmu) {
dev_dbg(dev, "missing pmu for %pOF, defer!\n", events_node);
return -EPROBE_DEFER;
}
dev_info(dev, "measure bandwidth with pmu %s\n", priv->pmu->name);
return imx8m_ddrc_perf_enable(priv);
}
static int imx8m_ddrc_init_freq_info(struct device *dev)
{
struct imx8m_ddrc *priv = dev_get_drvdata(dev);
......@@ -372,6 +482,13 @@ static int imx8m_ddrc_check_opps(struct device *dev)
static void imx8m_ddrc_exit(struct device *dev)
{
struct imx8m_ddrc *priv = dev_get_drvdata(dev);
imx8m_ddrc_perf_disable(priv);
if (priv->pmu)
put_device(priv->pmu->dev);
priv->pmu = NULL;
dev_pm_opp_of_remove_table(dev);
}
......@@ -379,6 +496,7 @@ static int imx8m_ddrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct imx8m_ddrc *priv;
struct device_node *events_node;
const char *gov = DEVFREQ_GOV_USERSPACE;
int ret;
......@@ -394,6 +512,15 @@ static int imx8m_ddrc_probe(struct platform_device *pdev)
return ret;
}
events_node = of_parse_phandle(dev->of_node, "fsl,ddr-pmu", 0);
if (events_node && of_device_is_available(events_node)) {
ret = imx8m_ddrc_init_events(dev, events_node);
of_node_put(events_node);
if (ret)
goto err;
gov = DEVFREQ_GOV_SIMPLE_ONDEMAND;
}
priv->dram_core = devm_clk_get(dev, "core");
if (IS_ERR(priv->dram_core)) {
ret = PTR_ERR(priv->dram_core);
......@@ -447,6 +574,9 @@ static int imx8m_ddrc_probe(struct platform_device *pdev)
return 0;
err:
imx8m_ddrc_perf_disable(priv);
if (priv->pmu)
put_device(priv->pmu->dev);
dev_pm_opp_of_remove_table(dev);
return ret;
}
......
......@@ -202,6 +202,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,
return -ENOMEM;
pmu->plat_device = pdev;
pmu->pmu.parent_dev = &pdev->dev;
ret = pmu_parse_irqs(pmu);
if (ret)
......
......@@ -673,6 +673,7 @@ static int ddr_perf_probe(struct platform_device *pdev)
goto ddr_perf_err;
}
pmu->pmu.parent_dev = &pdev->dev;
ret = perf_pmu_register(&pmu->pmu, name, -1);
if (ret)
goto ddr_perf_err;
......
......@@ -261,6 +261,7 @@ void devm_devfreq_unregister_notifier(struct device *dev,
struct devfreq *devfreq,
struct notifier_block *nb,
unsigned int list);
struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node);
struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index);
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
......@@ -414,6 +415,11 @@ static inline void devm_devfreq_unregister_notifier(struct device *dev,
{
}
static struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node)
{
return ERR_PTR(-ENODEV);
}
static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
int index)
{
......
......@@ -271,6 +271,7 @@ struct pmu {
struct module *module;
struct device *dev;
struct device *parent_dev;
const struct attribute_group **attr_groups;
const struct attribute_group **attr_update;
const char *name;
......@@ -937,6 +938,14 @@ extern void perf_event_itrace_started(struct perf_event *event);
extern int perf_pmu_register(struct pmu *pmu, const char *name, int type);
extern void perf_pmu_unregister(struct pmu *pmu);
#ifdef CONFIG_OF
extern struct pmu* perf_get_pmu_by_node(struct device_node *node);
#else
static inline struct pmu* perf_get_pmu_by_node(struct device_node *node) {
return NULL;
}
#endif
extern int perf_num_counters(void);
extern const char *perf_pmu_name(void);
extern void __perf_event_task_sched_in(struct task_struct *prev,
......
......@@ -10667,6 +10667,7 @@ static int pmu_dev_alloc(struct pmu *pmu)
dev_set_drvdata(pmu->dev, pmu);
pmu->dev->bus = &pmu_bus;
pmu->dev->parent = pmu->parent_dev;
pmu->dev->release = pmu_dev_release;
ret = device_add(pmu->dev);
if (ret)
......@@ -10696,6 +10697,30 @@ static int pmu_dev_alloc(struct pmu *pmu)
goto out;
}
#ifdef CONFIG_OF
static int perf_match_parent_of_node(struct device *dev, const void *np)
{
return dev->parent && dev->parent->of_node == np;
}
/**
* perf_get_pmu_by_node() - Fetch PMU instance via devicetree node
*
* @node: devicetree node for PMU
*
* Caller should put_device(pmu-dev) when done.
*/
struct pmu* perf_get_pmu_by_node(struct device_node *node)
{
struct device* dev;
dev = bus_find_device(&pmu_bus, NULL, node, perf_match_parent_of_node);
return dev ? dev_get_drvdata(dev) : NULL;
}
EXPORT_SYMBOL_GPL(perf_get_pmu_by_node);
#endif
static struct lock_class_key cpuctx_mutex;
static struct lock_class_key cpuctx_lock;
......
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