Loading Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,9 @@ Optional properties: Defaults to 0x17 if not specified. - qcom,inst-ev: The instruction count event that this monitor is supposed to measure. Defaults to 0x08 if not specified. - qcom,stall-cycle-ev: The stall cycle count that this monitor is supposed to measure. Assumes 100% stall if not specified. Example: Loading @@ -24,6 +27,7 @@ Example: qcom,target-dev = <&memlat0>; qcom,cachemiss-ev = <0x2A>; qcom,inst-ev = <0x08>; qcom,stall-cycle-ev = <0xE7>; qcom,core-dev-table = < 300000 1525>, < 499200 3143>, Loading Documentation/devicetree/bindings/devfreq/devfreq-simple-dev.txt +2 −2 Original line number Diff line number Diff line Loading @@ -9,12 +9,12 @@ Required properties: - compatible: Must be "devfreq-simple-dev" - clock-names: Must be "devfreq_clk" - clocks: Must refer to the clock that's fed to the device. - freq-tbl-khz: A list of usable frequencies (in KHz) for the device clock. Optional properties: - polling-ms: Polling interval for the device in milliseconds. Default: 50 - governor: Initial governor to user for the device. Default: "performance" - qcom,prepare-clk: Prepare the device clock during initialization. - freq-tbl-khz: A list of usable frequencies (in kHz) for the device clock. Example: Loading drivers/devfreq/arm-memlat-mon.c +27 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ enum ev_index { INST_IDX, CM_IDX, CYC_IDX, STALL_CYC_IDX, NUM_EVENTS }; #define INST_EV 0x08 Loading Loading @@ -92,12 +93,19 @@ static void read_perf_counters(int cpu, struct cpu_grp_info *cpu_grp) { struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu); struct dev_stats *devstats = to_devstats(cpu_grp, cpu); unsigned long cyc_cnt; unsigned long cyc_cnt, stall_cnt; devstats->inst_count = read_event(&cpustats->events[INST_IDX]); devstats->mem_count = read_event(&cpustats->events[CM_IDX]); cyc_cnt = read_event(&cpustats->events[CYC_IDX]); devstats->freq = compute_freq(cpustats, cyc_cnt); if (cpustats->events[STALL_CYC_IDX].pevent) { stall_cnt = read_event(&cpustats->events[STALL_CYC_IDX]); stall_cnt = min(stall_cnt, cyc_cnt); devstats->stall_pct = mult_frac(100, stall_cnt, cyc_cnt); } else { devstats->stall_pct = 100; } } static unsigned long get_cnt(struct memlat_hwmon *hw) Loading @@ -117,7 +125,10 @@ static void delete_events(struct cpu_pmu_stats *cpustats) for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) { cpustats->events[i].prev_count = 0; if (cpustats->events[i].pevent) { perf_event_release_kernel(cpustats->events[i].pevent); cpustats->events[i].pevent = NULL; } } } Loading @@ -135,6 +146,7 @@ static void stop_hwmon(struct memlat_hwmon *hw) devstats->inst_count = 0; devstats->mem_count = 0; devstats->freq = 0; devstats->stall_pct = 0; } } Loading @@ -159,6 +171,7 @@ static int set_events(struct cpu_grp_info *cpu_grp, int cpu) struct perf_event *pevent; struct perf_event_attr *attr; int err, i; unsigned int event_id; struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu); /* Allocate an attribute for event initialization */ Loading @@ -167,7 +180,11 @@ static int set_events(struct cpu_grp_info *cpu_grp, int cpu) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) { attr->config = cpu_grp->event_ids[i]; event_id = cpu_grp->event_ids[i]; if (!event_id) continue; attr->config = event_id; pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL); if (IS_ERR(pevent)) Loading Loading @@ -282,6 +299,13 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) } cpu_grp->event_ids[INST_IDX] = event_id; ret = of_property_read_u32(dev->of_node, "qcom,stall-cycle-ev", &event_id); if (ret) dev_dbg(dev, "Stall cycle event not specified. Event ignored.\n"); else cpu_grp->event_ids[STALL_CYC_IDX] = event_id; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; Loading drivers/devfreq/devfreq_simple_dev.c +39 −21 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2015, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2015, 2017, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "devfreq-simple-dev: " fmt Loading @@ -26,6 +26,7 @@ struct dev_data { struct clk *clk; struct devfreq *df; struct devfreq_dev_profile profile; bool freq_in_khz; }; static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, Loading Loading @@ -57,7 +58,7 @@ static int dev_target(struct device *dev, unsigned long *freq, u32 flags) find_freq(&d->profile, freq, flags); rfreq = clk_round_rate(d->clk, *freq * 1000); rfreq = clk_round_rate(d->clk, d->freq_in_khz ? *freq * 1000 : *freq); if (IS_ERR_VALUE(rfreq)) { dev_err(dev, "devfreq: Cannot find matching frequency for %lu\n", *freq); Loading @@ -75,39 +76,30 @@ static int dev_get_cur_freq(struct device *dev, unsigned long *freq) f = clk_get_rate(d->clk); if (IS_ERR_VALUE(f)) return f; *freq = f / 1000; *freq = d->freq_in_khz ? f / 1000 : f; return 0; } #define PROP_TBL "freq-tbl-khz" static int devfreq_clock_probe(struct platform_device *pdev) static int parse_freq_table(struct device *dev, struct dev_data *d) { struct device *dev = &pdev->dev; struct dev_data *d; struct devfreq_dev_profile *p; u32 *data, poll; const char *gov_name; struct devfreq_dev_profile *p = &d->profile; int ret, len, i, j; u32 *data; unsigned long f; d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; platform_set_drvdata(pdev, d); d->clk = devm_clk_get(dev, "devfreq_clk"); if (IS_ERR(d->clk)) return PTR_ERR(d->clk); if (!of_find_property(dev->of_node, PROP_TBL, &len)) return -EINVAL; if (!of_find_property(dev->of_node, PROP_TBL, &len)) { if (dev_pm_opp_get_opp_count(dev) <= 0) return -EPROBE_DEFER; return 0; } d->freq_in_khz = true; len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p = &d->profile; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) Loading @@ -134,6 +126,32 @@ static int devfreq_clock_probe(struct platform_device *pdev) return -EINVAL; } return 0; } static int devfreq_clock_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dev_data *d; struct devfreq_dev_profile *p; u32 poll; const char *gov_name; int ret; d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; platform_set_drvdata(pdev, d); d->clk = devm_clk_get(dev, "devfreq_clk"); if (IS_ERR(d->clk)) return PTR_ERR(d->clk); ret = parse_freq_table(dev, d); if (ret) return ret; p = &d->profile; p->target = dev_target; p->get_cur_freq = dev_get_cur_freq; ret = dev_get_cur_freq(dev, &p->initial_freq); Loading drivers/devfreq/governor_memlat.c +6 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ struct memlat_node { unsigned int ratio_ceil; unsigned int stall_floor; bool mon_started; bool already_zero; struct list_head list; Loading Loading @@ -239,9 +240,11 @@ static int devfreq_memlat_get_freq(struct devfreq *df, hw->core_stats[i].id, hw->core_stats[i].inst_count, hw->core_stats[i].mem_count, hw->core_stats[i].freq, ratio); hw->core_stats[i].freq, hw->core_stats[i].stall_pct, ratio); if (ratio <= node->ratio_ceil && hw->core_stats[i].stall_pct >= node->stall_floor && hw->core_stats[i].freq > max_freq) { lat_dev = i; max_freq = hw->core_stats[i].freq; Loading @@ -267,9 +270,11 @@ 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[] = { &dev_attr_ratio_ceil.attr, &dev_attr_stall_floor.attr, &dev_attr_freq_map.attr, NULL, }; Loading Loading
Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,9 @@ Optional properties: Defaults to 0x17 if not specified. - qcom,inst-ev: The instruction count event that this monitor is supposed to measure. Defaults to 0x08 if not specified. - qcom,stall-cycle-ev: The stall cycle count that this monitor is supposed to measure. Assumes 100% stall if not specified. Example: Loading @@ -24,6 +27,7 @@ Example: qcom,target-dev = <&memlat0>; qcom,cachemiss-ev = <0x2A>; qcom,inst-ev = <0x08>; qcom,stall-cycle-ev = <0xE7>; qcom,core-dev-table = < 300000 1525>, < 499200 3143>, Loading
Documentation/devicetree/bindings/devfreq/devfreq-simple-dev.txt +2 −2 Original line number Diff line number Diff line Loading @@ -9,12 +9,12 @@ Required properties: - compatible: Must be "devfreq-simple-dev" - clock-names: Must be "devfreq_clk" - clocks: Must refer to the clock that's fed to the device. - freq-tbl-khz: A list of usable frequencies (in KHz) for the device clock. Optional properties: - polling-ms: Polling interval for the device in milliseconds. Default: 50 - governor: Initial governor to user for the device. Default: "performance" - qcom,prepare-clk: Prepare the device clock during initialization. - freq-tbl-khz: A list of usable frequencies (in kHz) for the device clock. Example: Loading
drivers/devfreq/arm-memlat-mon.c +27 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ enum ev_index { INST_IDX, CM_IDX, CYC_IDX, STALL_CYC_IDX, NUM_EVENTS }; #define INST_EV 0x08 Loading Loading @@ -92,12 +93,19 @@ static void read_perf_counters(int cpu, struct cpu_grp_info *cpu_grp) { struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu); struct dev_stats *devstats = to_devstats(cpu_grp, cpu); unsigned long cyc_cnt; unsigned long cyc_cnt, stall_cnt; devstats->inst_count = read_event(&cpustats->events[INST_IDX]); devstats->mem_count = read_event(&cpustats->events[CM_IDX]); cyc_cnt = read_event(&cpustats->events[CYC_IDX]); devstats->freq = compute_freq(cpustats, cyc_cnt); if (cpustats->events[STALL_CYC_IDX].pevent) { stall_cnt = read_event(&cpustats->events[STALL_CYC_IDX]); stall_cnt = min(stall_cnt, cyc_cnt); devstats->stall_pct = mult_frac(100, stall_cnt, cyc_cnt); } else { devstats->stall_pct = 100; } } static unsigned long get_cnt(struct memlat_hwmon *hw) Loading @@ -117,7 +125,10 @@ static void delete_events(struct cpu_pmu_stats *cpustats) for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) { cpustats->events[i].prev_count = 0; if (cpustats->events[i].pevent) { perf_event_release_kernel(cpustats->events[i].pevent); cpustats->events[i].pevent = NULL; } } } Loading @@ -135,6 +146,7 @@ static void stop_hwmon(struct memlat_hwmon *hw) devstats->inst_count = 0; devstats->mem_count = 0; devstats->freq = 0; devstats->stall_pct = 0; } } Loading @@ -159,6 +171,7 @@ static int set_events(struct cpu_grp_info *cpu_grp, int cpu) struct perf_event *pevent; struct perf_event_attr *attr; int err, i; unsigned int event_id; struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu); /* Allocate an attribute for event initialization */ Loading @@ -167,7 +180,11 @@ static int set_events(struct cpu_grp_info *cpu_grp, int cpu) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) { attr->config = cpu_grp->event_ids[i]; event_id = cpu_grp->event_ids[i]; if (!event_id) continue; attr->config = event_id; pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL); if (IS_ERR(pevent)) Loading Loading @@ -282,6 +299,13 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) } cpu_grp->event_ids[INST_IDX] = event_id; ret = of_property_read_u32(dev->of_node, "qcom,stall-cycle-ev", &event_id); if (ret) dev_dbg(dev, "Stall cycle event not specified. Event ignored.\n"); else cpu_grp->event_ids[STALL_CYC_IDX] = event_id; for_each_cpu(cpu, &cpu_grp->cpus) to_devstats(cpu_grp, cpu)->id = cpu; Loading
drivers/devfreq/devfreq_simple_dev.c +39 −21 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2015, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2015, 2017, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "devfreq-simple-dev: " fmt Loading @@ -26,6 +26,7 @@ struct dev_data { struct clk *clk; struct devfreq *df; struct devfreq_dev_profile profile; bool freq_in_khz; }; static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, Loading Loading @@ -57,7 +58,7 @@ static int dev_target(struct device *dev, unsigned long *freq, u32 flags) find_freq(&d->profile, freq, flags); rfreq = clk_round_rate(d->clk, *freq * 1000); rfreq = clk_round_rate(d->clk, d->freq_in_khz ? *freq * 1000 : *freq); if (IS_ERR_VALUE(rfreq)) { dev_err(dev, "devfreq: Cannot find matching frequency for %lu\n", *freq); Loading @@ -75,39 +76,30 @@ static int dev_get_cur_freq(struct device *dev, unsigned long *freq) f = clk_get_rate(d->clk); if (IS_ERR_VALUE(f)) return f; *freq = f / 1000; *freq = d->freq_in_khz ? f / 1000 : f; return 0; } #define PROP_TBL "freq-tbl-khz" static int devfreq_clock_probe(struct platform_device *pdev) static int parse_freq_table(struct device *dev, struct dev_data *d) { struct device *dev = &pdev->dev; struct dev_data *d; struct devfreq_dev_profile *p; u32 *data, poll; const char *gov_name; struct devfreq_dev_profile *p = &d->profile; int ret, len, i, j; u32 *data; unsigned long f; d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; platform_set_drvdata(pdev, d); d->clk = devm_clk_get(dev, "devfreq_clk"); if (IS_ERR(d->clk)) return PTR_ERR(d->clk); if (!of_find_property(dev->of_node, PROP_TBL, &len)) return -EINVAL; if (!of_find_property(dev->of_node, PROP_TBL, &len)) { if (dev_pm_opp_get_opp_count(dev) <= 0) return -EPROBE_DEFER; return 0; } d->freq_in_khz = true; len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p = &d->profile; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) Loading @@ -134,6 +126,32 @@ static int devfreq_clock_probe(struct platform_device *pdev) return -EINVAL; } return 0; } static int devfreq_clock_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dev_data *d; struct devfreq_dev_profile *p; u32 poll; const char *gov_name; int ret; d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; platform_set_drvdata(pdev, d); d->clk = devm_clk_get(dev, "devfreq_clk"); if (IS_ERR(d->clk)) return PTR_ERR(d->clk); ret = parse_freq_table(dev, d); if (ret) return ret; p = &d->profile; p->target = dev_target; p->get_cur_freq = dev_get_cur_freq; ret = dev_get_cur_freq(dev, &p->initial_freq); Loading
drivers/devfreq/governor_memlat.c +6 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ struct memlat_node { unsigned int ratio_ceil; unsigned int stall_floor; bool mon_started; bool already_zero; struct list_head list; Loading Loading @@ -239,9 +240,11 @@ static int devfreq_memlat_get_freq(struct devfreq *df, hw->core_stats[i].id, hw->core_stats[i].inst_count, hw->core_stats[i].mem_count, hw->core_stats[i].freq, ratio); hw->core_stats[i].freq, hw->core_stats[i].stall_pct, ratio); if (ratio <= node->ratio_ceil && hw->core_stats[i].stall_pct >= node->stall_floor && hw->core_stats[i].freq > max_freq) { lat_dev = i; max_freq = hw->core_stats[i].freq; Loading @@ -267,9 +270,11 @@ 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[] = { &dev_attr_ratio_ceil.attr, &dev_attr_stall_floor.attr, &dev_attr_freq_map.attr, NULL, }; Loading