Loading Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt +1 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ arm-memlat-mon is a device that represents the use of the PMU in ARM cores to measure the parameters for latency driven memory access patterns. Required properties: - compatible: Must be "qcom,arm-memlat-mon" - compatible: Must be "qcom,arm-memlat-mon" or "qcom,arm-cpu-mon" - qcom,cpulist: List of CPU phandles to be monitored in a cluster - qcom,target-dev: The DT device that corresponds to this master port - qcom,core-dev-table: A mapping table of core frequency to a required bandwidth vote at the Loading drivers/devfreq/arm-memlat-mon.c +34 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include "governor.h" #include "governor_memlat.h" #include <linux/perf_event.h> #include <linux/of_device.h> enum ev_index { INST_IDX, Loading Loading @@ -52,6 +53,10 @@ struct cpu_grp_info { struct memlat_hwmon hw; }; struct memlat_mon_spec { bool is_compute; }; #define to_cpustats(cpu_grp, cpu) \ (&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)]) #define to_devstats(cpu_grp, cpu) \ Loading Loading @@ -83,6 +88,9 @@ static inline unsigned long read_event(struct event_data *event) unsigned long ev_count; u64 total, enabled, running; if (!event->pevent) return 0; total = perf_event_read_value(event->pevent, &enabled, &running); ev_count = total - event->prev_count; event->prev_count = total; Loading Loading @@ -249,6 +257,7 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct memlat_hwmon *hw; struct cpu_grp_info *cpu_grp; const struct memlat_mon_spec *spec; int cpu, ret; u32 event_id; Loading Loading @@ -282,6 +291,22 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) cpu_grp->event_ids[CYC_IDX] = CYC_EV; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; hw->start_hwmon = &start_hwmon; hw->stop_hwmon = &stop_hwmon; hw->get_cnt = &get_cnt; spec = of_device_get_match_data(dev); if (spec && spec->is_compute) { ret = register_compute(dev, hw); if (ret) pr_err("Compute Gov registration failed\n"); return ret; } ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev", &event_id); if (ret) { Loading @@ -306,24 +331,21 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) else cpu_grp->event_ids[STALL_CYC_IDX] = event_id; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; hw->start_hwmon = &start_hwmon; hw->stop_hwmon = &stop_hwmon; hw->get_cnt = &get_cnt; ret = register_memlat(dev, hw); if (ret) { if (ret) pr_err("Mem Latency Gov registration failed\n"); return ret; } return 0; } static const struct memlat_mon_spec spec[] = { [0] = { false }, [1] = { true }, }; static const struct of_device_id memlat_match_table[] = { { .compatible = "qcom,arm-memlat-mon" }, { .compatible = "qcom,arm-memlat-mon", .data = &spec[0] }, { .compatible = "qcom,arm-cpu-mon", .data = &spec[1] }, {} }; Loading drivers/devfreq/bimc-bwmon.c +17 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "bimc-bwmon: " fmt Loading Loading @@ -168,6 +168,14 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) writel_relaxed(MON_CLEAR_ALL_BIT, MON3_CLEAR(m)); else writel_relaxed(MON_CLEAR_BIT, MON3_CLEAR(m)); /* * In some hardware versions since MON3_CLEAR(m) register does * not have self-clearing capability it needs to be cleared * explicitly. But we also need to ensure the writes to it * are successful before clearing it. */ wmb(); writel_relaxed(0, MON3_CLEAR(m)); break; } /* Loading Loading @@ -357,6 +365,14 @@ void mon_irq_clear(struct bwmon *m, enum mon_reg_type type) break; case MON3: writel_relaxed(MON3_INT_STATUS_MASK, MON3_INT_CLR(m)); /* * In some hardware versions since MON3_INT_CLEAR(m) register * does not have self-clearing capability it needs to be * cleared explicitly. But we also need to ensure the writes * to it are successful before clearing it. */ wmb(); writel_relaxed(0, MON3_INT_CLR(m)); break; } } Loading drivers/devfreq/devfreq.c +1 −3 Original line number Diff line number Diff line Loading @@ -560,7 +560,6 @@ struct devfreq *devfreq_add_device(struct device *dev, { struct devfreq *devfreq; struct devfreq_governor *governor; static atomic_t devfreq_no = ATOMIC_INIT(-1); int err = 0; if (!dev || !profile || !governor_name) { Loading Loading @@ -620,8 +619,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->max_freq = devfreq->scaling_max_freq; dev_set_name(&devfreq->dev, "devfreq%d", atomic_inc_return(&devfreq_no)); dev_set_name(&devfreq->dev, "%s", dev_name(dev)); err = device_register(&devfreq->dev); if (err) { mutex_unlock(&devfreq->lock); Loading drivers/devfreq/governor_memlat.c +77 −16 Original line number Diff line number Diff line Loading @@ -40,7 +40,8 @@ struct memlat_node { static LIST_HEAD(memlat_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static int memlat_use_cnt; static int compute_use_cnt; static DEFINE_MUTEX(state_lock); #define show_attr(name) \ Loading Loading @@ -232,8 +233,7 @@ static int devfreq_memlat_get_freq(struct devfreq *df, if (hw->core_stats[i].mem_count) ratio /= hw->core_stats[i].mem_count; if (!hw->core_stats[i].inst_count || !hw->core_stats[i].freq) if (!hw->core_stats[i].freq) continue; trace_memlat_dev_meas(dev_name(df->dev.parent), Loading Loading @@ -272,16 +272,26 @@ static int devfreq_memlat_get_freq(struct devfreq *df, gov_attr(ratio_ceil, 1U, 10000U); gov_attr(stall_floor, 0U, 100U); static struct attribute *dev_attr[] = { static struct attribute *memlat_dev_attr[] = { &dev_attr_ratio_ceil.attr, &dev_attr_stall_floor.attr, &dev_attr_freq_map.attr, NULL, }; static struct attribute_group dev_attr_group = { static struct attribute *compute_dev_attr[] = { &dev_attr_freq_map.attr, NULL, }; static struct attribute_group memlat_dev_attr_group = { .name = "mem_latency", .attrs = dev_attr, .attrs = memlat_dev_attr, }; static struct attribute_group compute_dev_attr_group = { .name = "compute", .attrs = compute_dev_attr, }; #define MIN_MS 10U Loading Loading @@ -330,6 +340,12 @@ static struct devfreq_governor devfreq_gov_memlat = { .event_handler = devfreq_memlat_ev_handler, }; static struct devfreq_governor devfreq_gov_compute = { .name = "compute", .get_target_freq = devfreq_memlat_get_freq, .event_handler = devfreq_memlat_ev_handler, }; #define NUM_COLS 2 static struct core_dev_map *init_core_dev_map(struct device *dev, char *prop_name) Loading Loading @@ -372,20 +388,17 @@ static struct core_dev_map *init_core_dev_map(struct device *dev, return tbl; } int register_memlat(struct device *dev, struct memlat_hwmon *hw) static struct memlat_node *register_common(struct device *dev, struct memlat_hwmon *hw) { int ret = 0; struct memlat_node *node; if (!hw->dev && !hw->of_node) return -EINVAL; return ERR_PTR(-EINVAL); node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; node->gov = &devfreq_gov_memlat; node->attr_grp = &dev_attr_group; return ERR_PTR(-ENOMEM); node->ratio_ceil = 10; node->hw = hw; Loading @@ -393,20 +406,68 @@ int register_memlat(struct device *dev, struct memlat_hwmon *hw) hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table"); if (!hw->freq_map) { dev_err(dev, "Couldn't find the core-dev freq table!\n"); return -EINVAL; return ERR_PTR(-EINVAL); } mutex_lock(&list_lock); list_add_tail(&node->list, &memlat_list); mutex_unlock(&list_lock); return node; } int register_compute(struct device *dev, struct memlat_hwmon *hw) { struct memlat_node *node; int ret = 0; node = register_common(dev, hw); if (IS_ERR(node)) { ret = PTR_ERR(node); goto out; } mutex_lock(&state_lock); if (!use_cnt) node->gov = &devfreq_gov_compute; node->attr_grp = &compute_dev_attr_group; if (!compute_use_cnt) ret = devfreq_add_governor(&devfreq_gov_compute); if (!ret) compute_use_cnt++; mutex_unlock(&state_lock); out: if (!ret) dev_info(dev, "Compute governor registered.\n"); else dev_err(dev, "Compute governor registration failed!\n"); return ret; } int register_memlat(struct device *dev, struct memlat_hwmon *hw) { struct memlat_node *node; int ret = 0; node = register_common(dev, hw); if (IS_ERR(node)) { ret = PTR_ERR(node); goto out; } mutex_lock(&state_lock); node->gov = &devfreq_gov_memlat; node->attr_grp = &memlat_dev_attr_group; if (!memlat_use_cnt) ret = devfreq_add_governor(&devfreq_gov_memlat); if (!ret) use_cnt++; memlat_use_cnt++; mutex_unlock(&state_lock); out: if (!ret) dev_info(dev, "Memory Latency governor registered.\n"); else Loading Loading
Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt +1 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ arm-memlat-mon is a device that represents the use of the PMU in ARM cores to measure the parameters for latency driven memory access patterns. Required properties: - compatible: Must be "qcom,arm-memlat-mon" - compatible: Must be "qcom,arm-memlat-mon" or "qcom,arm-cpu-mon" - qcom,cpulist: List of CPU phandles to be monitored in a cluster - qcom,target-dev: The DT device that corresponds to this master port - qcom,core-dev-table: A mapping table of core frequency to a required bandwidth vote at the Loading
drivers/devfreq/arm-memlat-mon.c +34 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include "governor.h" #include "governor_memlat.h" #include <linux/perf_event.h> #include <linux/of_device.h> enum ev_index { INST_IDX, Loading Loading @@ -52,6 +53,10 @@ struct cpu_grp_info { struct memlat_hwmon hw; }; struct memlat_mon_spec { bool is_compute; }; #define to_cpustats(cpu_grp, cpu) \ (&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)]) #define to_devstats(cpu_grp, cpu) \ Loading Loading @@ -83,6 +88,9 @@ static inline unsigned long read_event(struct event_data *event) unsigned long ev_count; u64 total, enabled, running; if (!event->pevent) return 0; total = perf_event_read_value(event->pevent, &enabled, &running); ev_count = total - event->prev_count; event->prev_count = total; Loading Loading @@ -249,6 +257,7 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct memlat_hwmon *hw; struct cpu_grp_info *cpu_grp; const struct memlat_mon_spec *spec; int cpu, ret; u32 event_id; Loading Loading @@ -282,6 +291,22 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) cpu_grp->event_ids[CYC_IDX] = CYC_EV; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; hw->start_hwmon = &start_hwmon; hw->stop_hwmon = &stop_hwmon; hw->get_cnt = &get_cnt; spec = of_device_get_match_data(dev); if (spec && spec->is_compute) { ret = register_compute(dev, hw); if (ret) pr_err("Compute Gov registration failed\n"); return ret; } ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev", &event_id); if (ret) { Loading @@ -306,24 +331,21 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) else cpu_grp->event_ids[STALL_CYC_IDX] = event_id; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; hw->start_hwmon = &start_hwmon; hw->stop_hwmon = &stop_hwmon; hw->get_cnt = &get_cnt; ret = register_memlat(dev, hw); if (ret) { if (ret) pr_err("Mem Latency Gov registration failed\n"); return ret; } return 0; } static const struct memlat_mon_spec spec[] = { [0] = { false }, [1] = { true }, }; static const struct of_device_id memlat_match_table[] = { { .compatible = "qcom,arm-memlat-mon" }, { .compatible = "qcom,arm-memlat-mon", .data = &spec[0] }, { .compatible = "qcom,arm-cpu-mon", .data = &spec[1] }, {} }; Loading
drivers/devfreq/bimc-bwmon.c +17 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "bimc-bwmon: " fmt Loading Loading @@ -168,6 +168,14 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) writel_relaxed(MON_CLEAR_ALL_BIT, MON3_CLEAR(m)); else writel_relaxed(MON_CLEAR_BIT, MON3_CLEAR(m)); /* * In some hardware versions since MON3_CLEAR(m) register does * not have self-clearing capability it needs to be cleared * explicitly. But we also need to ensure the writes to it * are successful before clearing it. */ wmb(); writel_relaxed(0, MON3_CLEAR(m)); break; } /* Loading Loading @@ -357,6 +365,14 @@ void mon_irq_clear(struct bwmon *m, enum mon_reg_type type) break; case MON3: writel_relaxed(MON3_INT_STATUS_MASK, MON3_INT_CLR(m)); /* * In some hardware versions since MON3_INT_CLEAR(m) register * does not have self-clearing capability it needs to be * cleared explicitly. But we also need to ensure the writes * to it are successful before clearing it. */ wmb(); writel_relaxed(0, MON3_INT_CLR(m)); break; } } Loading
drivers/devfreq/devfreq.c +1 −3 Original line number Diff line number Diff line Loading @@ -560,7 +560,6 @@ struct devfreq *devfreq_add_device(struct device *dev, { struct devfreq *devfreq; struct devfreq_governor *governor; static atomic_t devfreq_no = ATOMIC_INIT(-1); int err = 0; if (!dev || !profile || !governor_name) { Loading Loading @@ -620,8 +619,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->max_freq = devfreq->scaling_max_freq; dev_set_name(&devfreq->dev, "devfreq%d", atomic_inc_return(&devfreq_no)); dev_set_name(&devfreq->dev, "%s", dev_name(dev)); err = device_register(&devfreq->dev); if (err) { mutex_unlock(&devfreq->lock); Loading
drivers/devfreq/governor_memlat.c +77 −16 Original line number Diff line number Diff line Loading @@ -40,7 +40,8 @@ struct memlat_node { static LIST_HEAD(memlat_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static int memlat_use_cnt; static int compute_use_cnt; static DEFINE_MUTEX(state_lock); #define show_attr(name) \ Loading Loading @@ -232,8 +233,7 @@ static int devfreq_memlat_get_freq(struct devfreq *df, if (hw->core_stats[i].mem_count) ratio /= hw->core_stats[i].mem_count; if (!hw->core_stats[i].inst_count || !hw->core_stats[i].freq) if (!hw->core_stats[i].freq) continue; trace_memlat_dev_meas(dev_name(df->dev.parent), Loading Loading @@ -272,16 +272,26 @@ static int devfreq_memlat_get_freq(struct devfreq *df, gov_attr(ratio_ceil, 1U, 10000U); gov_attr(stall_floor, 0U, 100U); static struct attribute *dev_attr[] = { static struct attribute *memlat_dev_attr[] = { &dev_attr_ratio_ceil.attr, &dev_attr_stall_floor.attr, &dev_attr_freq_map.attr, NULL, }; static struct attribute_group dev_attr_group = { static struct attribute *compute_dev_attr[] = { &dev_attr_freq_map.attr, NULL, }; static struct attribute_group memlat_dev_attr_group = { .name = "mem_latency", .attrs = dev_attr, .attrs = memlat_dev_attr, }; static struct attribute_group compute_dev_attr_group = { .name = "compute", .attrs = compute_dev_attr, }; #define MIN_MS 10U Loading Loading @@ -330,6 +340,12 @@ static struct devfreq_governor devfreq_gov_memlat = { .event_handler = devfreq_memlat_ev_handler, }; static struct devfreq_governor devfreq_gov_compute = { .name = "compute", .get_target_freq = devfreq_memlat_get_freq, .event_handler = devfreq_memlat_ev_handler, }; #define NUM_COLS 2 static struct core_dev_map *init_core_dev_map(struct device *dev, char *prop_name) Loading Loading @@ -372,20 +388,17 @@ static struct core_dev_map *init_core_dev_map(struct device *dev, return tbl; } int register_memlat(struct device *dev, struct memlat_hwmon *hw) static struct memlat_node *register_common(struct device *dev, struct memlat_hwmon *hw) { int ret = 0; struct memlat_node *node; if (!hw->dev && !hw->of_node) return -EINVAL; return ERR_PTR(-EINVAL); node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; node->gov = &devfreq_gov_memlat; node->attr_grp = &dev_attr_group; return ERR_PTR(-ENOMEM); node->ratio_ceil = 10; node->hw = hw; Loading @@ -393,20 +406,68 @@ int register_memlat(struct device *dev, struct memlat_hwmon *hw) hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table"); if (!hw->freq_map) { dev_err(dev, "Couldn't find the core-dev freq table!\n"); return -EINVAL; return ERR_PTR(-EINVAL); } mutex_lock(&list_lock); list_add_tail(&node->list, &memlat_list); mutex_unlock(&list_lock); return node; } int register_compute(struct device *dev, struct memlat_hwmon *hw) { struct memlat_node *node; int ret = 0; node = register_common(dev, hw); if (IS_ERR(node)) { ret = PTR_ERR(node); goto out; } mutex_lock(&state_lock); if (!use_cnt) node->gov = &devfreq_gov_compute; node->attr_grp = &compute_dev_attr_group; if (!compute_use_cnt) ret = devfreq_add_governor(&devfreq_gov_compute); if (!ret) compute_use_cnt++; mutex_unlock(&state_lock); out: if (!ret) dev_info(dev, "Compute governor registered.\n"); else dev_err(dev, "Compute governor registration failed!\n"); return ret; } int register_memlat(struct device *dev, struct memlat_hwmon *hw) { struct memlat_node *node; int ret = 0; node = register_common(dev, hw); if (IS_ERR(node)) { ret = PTR_ERR(node); goto out; } mutex_lock(&state_lock); node->gov = &devfreq_gov_memlat; node->attr_grp = &memlat_dev_attr_group; if (!memlat_use_cnt) ret = devfreq_add_governor(&devfreq_gov_memlat); if (!ret) use_cnt++; memlat_use_cnt++; mutex_unlock(&state_lock); out: if (!ret) dev_info(dev, "Memory Latency governor registered.\n"); else Loading