Loading drivers/devfreq/bimc-bwmon.c +48 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/spinlock.h> #include "governor_bw_hwmon.h" Loading @@ -31,11 +32,17 @@ #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) struct bwmon_spec { bool wrap_on_thres; bool overflow; }; struct bwmon { void __iomem *base; void __iomem *global_base; unsigned int mport; unsigned int irq; const struct bwmon_spec *spec; struct device *dev; struct bw_hwmon hw; }; Loading Loading @@ -94,7 +101,7 @@ static void mon_irq_disable(struct bwmon *m) writel_relaxed(val, MON_INT_EN(m)); } static int mon_irq_status(struct bwmon *m) static unsigned int mon_irq_status(struct bwmon *m) { u32 mval; Loading @@ -103,12 +110,12 @@ static int mon_irq_status(struct bwmon *m) dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); return mval & 0x1; return mval; } static void mon_irq_clear(struct bwmon *m) { writel_relaxed(0x1, MON_INT_CLR(m)); writel_relaxed(0x3, MON_INT_CLR(m)); /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); Loading @@ -127,14 +134,22 @@ static u32 mon_get_limit(struct bwmon *m) return readl_relaxed(MON_THRES(m)); } #define THRES_HIT(status) (status & BIT(0)) #define OVERFLOW(status) (status & BIT(1)) static unsigned long mon_get_count(struct bwmon *m) { unsigned long count; unsigned long count, status; count = readl_relaxed(MON_CNT(m)); status = mon_irq_status(m); dev_dbg(m->dev, "Counter: %08lx\n", count); if (mon_irq_status(m)) if (OVERFLOW(status) && m->spec->overflow) count += 0xFFFFFFFF; if (THRES_HIT(status) && m->spec->wrap_on_thres) count += mon_get_limit(m); dev_dbg(m->dev, "Actual Count: %08lx\n", count); return count; Loading Loading @@ -173,11 +188,17 @@ static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw, mbps = mon_get_count(m); mbps = bytes_to_mbps(mbps, us); /* * The fudging of mbps when calculating limit is to workaround a HW * design issue. Needs further tuning. * If the counter wraps on thres, don't set the thres too low. * Setting it too low runs the risk of the counter wrapping around * multiple times before the IRQ is processed. */ if (likely(!m->spec->wrap_on_thres)) limit = mbps_to_bytes(mbps, sample_ms, tol); else limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); mon_set_limit(m, limit); mon_clear(m); Loading Loading @@ -273,11 +294,23 @@ static int resume_bw_hwmon(struct bw_hwmon *hw) /*************************************************************************/ static const struct bwmon_spec spec[] = { { .wrap_on_thres = true, .overflow = false }, { .wrap_on_thres = false, .overflow = true }, }; static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon", .data = &spec[0] }, { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, {} }; static int bimc_bwmon_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct bwmon *m; const struct of_device_id *id; int ret; u32 data; Loading @@ -293,6 +326,13 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) } m->mport = data; id = of_match_device(bimc_bwmon_match_table, dev); if (!id) { dev_err(dev, "Unknown device type!\n"); return -ENODEV; } m->spec = id->data; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); Loading Loading @@ -339,11 +379,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return 0; } static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon" }, {} }; static struct platform_driver bimc_bwmon_driver = { .probe = bimc_bwmon_driver_probe, .driver = { Loading drivers/devfreq/governor_cache_hwmon.c +170 −79 Original line number Diff line number Diff line Loading @@ -20,14 +20,41 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/devfreq.h> #include <trace/events/power.h> #include "governor.h" #include "governor_cache_hwmon.h" struct cache_hwmon_node { unsigned int cycles_per_low_req; unsigned int cycles_per_med_req; unsigned int cycles_per_high_req; unsigned int min_busy; unsigned int max_busy; unsigned int tolerance_mrps; unsigned int guard_band_mhz; unsigned int decay_rate; unsigned long prev_mhz; ktime_t prev_ts; bool mon_started; struct list_head list; void *orig_data; struct cache_hwmon *hw; struct attribute_group *attr_grp; }; static LIST_HEAD(cache_hwmon_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(state_lock); #define show_attr(name) \ static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ return scnprintf(buf, PAGE_SIZE, "%u\n", name); \ struct devfreq *df = to_devfreq(dev); \ struct cache_hwmon_node *hw = df->data; \ return scnprintf(buf, PAGE_SIZE, "%u\n", hw->name); \ } #define store_attr(name, _min, _max) \ Loading @@ -37,12 +64,14 @@ static ssize_t store_##name(struct device *dev, \ { \ int ret; \ unsigned int val; \ struct devfreq *df = to_devfreq(dev); \ struct cache_hwmon_node *hw = df->data; \ ret = kstrtoint(buf, 10, &val); \ if (ret) \ return ret; \ val = max(val, _min); \ val = min(val, _max); \ name = val; \ hw->name = val; \ return count; \ } Loading @@ -51,28 +80,31 @@ show_attr(__attr) \ store_attr(__attr, (min), (max)) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) static struct cache_hwmon *hw; static unsigned int cycles_per_low_req; static unsigned int cycles_per_med_req = 20; static unsigned int cycles_per_high_req = 35; static unsigned int min_busy = 100; static unsigned int max_busy = 100; static unsigned int tolerance_mrps = 5; static unsigned int guard_band_mhz = 100; static unsigned int decay_rate = 90; #define MIN_MS 10U #define MAX_MS 500U static unsigned int sample_ms = 50; static unsigned long prev_mhz; static ktime_t prev_ts; static unsigned long measure_mrps_and_set_irq(struct devfreq *df, static struct cache_hwmon_node *find_hwmon_node(struct devfreq *df) { struct cache_hwmon_node *node, *found = NULL; mutex_lock(&list_lock); list_for_each_entry(node, &cache_hwmon_list, list) if (node->hw->dev == df->dev.parent || node->hw->of_node == df->dev.parent->of_node) { found = node; break; } mutex_unlock(&list_lock); return found; } static unsigned long measure_mrps_and_set_irq(struct cache_hwmon_node *node, struct mrps_stats *stat) { ktime_t ts; unsigned int us; struct cache_hwmon *hw = node->hw; /* * Since we are stopping the counters, we don't want this short work Loading @@ -84,59 +116,70 @@ static unsigned long measure_mrps_and_set_irq(struct devfreq *df, preempt_disable(); ts = ktime_get(); us = ktime_to_us(ktime_sub(ts, prev_ts)); us = ktime_to_us(ktime_sub(ts, node->prev_ts)); if (!us) us = 1; hw->meas_mrps_and_set_irq(df, tolerance_mrps, us, stat); prev_ts = ts; hw->meas_mrps_and_set_irq(hw, node->tolerance_mrps, us, stat); node->prev_ts = ts; preempt_enable(); pr_debug("stat H=%3lu, M=%3lu, T=%3lu, b=%3u, f=%4lu, us=%d\n", stat->high, stat->med, stat->high + stat->med, stat->busy_percent, df->previous_freq / 1000, us); trace_cache_hwmon_meas(dev_name(hw->df->dev.parent), stat->mrps[HIGH], stat->mrps[MED], stat->mrps[LOW], stat->busy_percent, us); return 0; } static void compute_cache_freq(struct mrps_stats *mrps, unsigned long *freq) static void compute_cache_freq(struct cache_hwmon_node *node, struct mrps_stats *mrps, unsigned long *freq) { unsigned long new_mhz; unsigned int busy; new_mhz = mrps->high * cycles_per_high_req + mrps->med * cycles_per_med_req + mrps->low * cycles_per_low_req; new_mhz = mrps->mrps[HIGH] * node->cycles_per_high_req + mrps->mrps[MED] * node->cycles_per_med_req + mrps->mrps[LOW] * node->cycles_per_low_req; busy = max(min_busy, mrps->busy_percent); busy = min(max_busy, busy); busy = max(node->min_busy, mrps->busy_percent); busy = min(node->max_busy, busy); new_mhz *= 100; new_mhz /= busy; if (new_mhz < prev_mhz) { new_mhz = new_mhz * decay_rate + prev_mhz * (100 - decay_rate); if (new_mhz < node->prev_mhz) { new_mhz = new_mhz * node->decay_rate + node->prev_mhz * (100 - node->decay_rate); new_mhz /= 100; } prev_mhz = new_mhz; node->prev_mhz = new_mhz; new_mhz += guard_band_mhz; new_mhz += node->guard_band_mhz; *freq = new_mhz * 1000; trace_cache_hwmon_update(dev_name(node->hw->df->dev.parent), *freq); } #define TOO_SOON_US (1 * USEC_PER_MSEC) static irqreturn_t mon_intr_handler(int irq, void *dev) int update_cache_hwmon(struct cache_hwmon *hwmon) { struct devfreq *df = dev; struct cache_hwmon_node *node; struct devfreq *df; ktime_t ts; unsigned int us; int ret; if (!hw->is_valid_irq(df)) return IRQ_NONE; pr_debug("Got interrupt\n"); if (!hwmon) return -EINVAL; df = hwmon->df; if (!df) return -ENODEV; node = df->data; if (!node) return -ENODEV; if (!node->mon_started) return -EBUSY; dev_dbg(df->dev.parent, "Got update request\n"); devfreq_monitor_stop(df); /* Loading @@ -152,27 +195,30 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) * readjusted. */ ts = ktime_get(); us = ktime_to_us(ktime_sub(ts, prev_ts)); us = ktime_to_us(ktime_sub(ts, node->prev_ts)); if (us > TOO_SOON_US) { mutex_lock(&df->lock); ret = update_devfreq(df); if (ret) pr_err("Unable to update freq on IRQ!\n"); dev_err(df->dev.parent, "Unable to update freq on request!\n"); mutex_unlock(&df->lock); } devfreq_monitor_start(df); return IRQ_HANDLED; return 0; } static int devfreq_cache_hwmon_get_freq(struct devfreq *df, unsigned long *freq) { struct mrps_stats stat; struct cache_hwmon_node *node = df->data; measure_mrps_and_set_irq(df, &stat); compute_cache_freq(&stat, freq); memset(&stat, 0, sizeof(stat)); measure_mrps_and_set_irq(node, &stat); compute_cache_freq(node, &stat, freq); return 0; } Loading Loading @@ -207,58 +253,73 @@ static int start_monitoring(struct devfreq *df) { int ret; struct mrps_stats mrps; prev_ts = ktime_get(); prev_mhz = 0; mrps.high = (df->previous_freq / 1000) - guard_band_mhz; mrps.high /= cycles_per_high_req; ret = hw->start_hwmon(df, &mrps); struct device *dev = df->dev.parent; struct cache_hwmon_node *node; struct cache_hwmon *hw; node = find_hwmon_node(df); if (!node) { dev_err(dev, "Unable to find HW monitor!\n"); return -ENODEV; } hw = node->hw; hw->df = df; node->orig_data = df->data; df->data = node; node->prev_ts = ktime_get(); node->prev_mhz = 0; mrps.mrps[HIGH] = (df->previous_freq / 1000) - node->guard_band_mhz; mrps.mrps[HIGH] /= node->cycles_per_high_req; mrps.mrps[MED] = mrps.mrps[LOW] = 0; ret = hw->start_hwmon(hw, &mrps); if (ret) { pr_err("Unable to start HW monitor!\n"); return ret; dev_err(dev, "Unable to start HW monitor!\n"); goto err_start; } devfreq_monitor_start(df); ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, "cache_hwmon", df); if (ret) { pr_err("Unable to register interrupt handler!\n"); goto req_irq_fail; } node->mon_started = true; ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) { pr_err("Error creating sys entries!\n"); dev_err(dev, "Error creating sys entries!\n"); goto sysfs_fail; } return 0; sysfs_fail: disable_irq(hw->irq); free_irq(hw->irq, df); req_irq_fail: node->mon_started = false; devfreq_monitor_stop(df); hw->stop_hwmon(df); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; return ret; } static void stop_monitoring(struct devfreq *df) { struct cache_hwmon_node *node = df->data; struct cache_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, &dev_attr_group); disable_irq(hw->irq); free_irq(hw->irq, df); node->mon_started = false; devfreq_monitor_stop(df); hw->stop_hwmon(df); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; } static int devfreq_cache_hwmon_ev_handler(struct devfreq *df, unsigned int event, void *data) { int ret; unsigned int sample_ms; switch (event) { case DEVFREQ_GOV_START: Loading @@ -271,12 +332,12 @@ static int devfreq_cache_hwmon_ev_handler(struct devfreq *df, if (ret) return ret; pr_debug("Enabled Cache HW monitor governor\n"); dev_dbg(df->dev.parent, "Enabled Cache HW monitor governor\n"); break; case DEVFREQ_GOV_STOP: stop_monitoring(df); pr_debug("Disabled Cache HW monitor governor\n"); dev_dbg(df->dev.parent, "Disabled Cache HW monitor governor\n"); break; case DEVFREQ_GOV_INTERVAL: Loading @@ -296,18 +357,48 @@ static struct devfreq_governor devfreq_cache_hwmon = { .event_handler = devfreq_cache_hwmon_ev_handler, }; int register_cache_hwmon(struct cache_hwmon *hwmon) int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) { int ret; hw = hwmon; int ret = 0; struct cache_hwmon_node *node; if (!hwmon->dev && !hwmon->of_node) return -EINVAL; node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; node->cycles_per_med_req = 20; node->cycles_per_high_req = 35; node->min_busy = 100; node->max_busy = 100; node->tolerance_mrps = 5; node->guard_band_mhz = 100; node->decay_rate = 90; node->hw = hwmon; node->attr_grp = &dev_attr_group; mutex_lock(&state_lock); if (!use_cnt) { ret = devfreq_add_governor(&devfreq_cache_hwmon); if (ret) { pr_err("devfreq governor registration failed\n"); if (!ret) use_cnt++; } mutex_unlock(&state_lock); if (!ret) { dev_info(dev, "Cache HWmon governor registered.\n"); } else { dev_err(dev, "Failed to add Cache HWmon governor\n"); return ret; } return 0; mutex_lock(&list_lock); list_add_tail(&node->list, &cache_hwmon_list); mutex_unlock(&list_lock); return ret; } MODULE_DESCRIPTION("HW monitor based cache freq driver"); Loading drivers/devfreq/governor_cache_hwmon.h +35 −10 Original line number Diff line number Diff line Loading @@ -9,27 +9,52 @@ #include <linux/kernel.h> #include <linux/devfreq.h> enum request_group { HIGH, MED, LOW, MAX_NUM_GROUPS, }; struct mrps_stats { unsigned long high; unsigned long med; unsigned long low; unsigned long mrps[MAX_NUM_GROUPS]; unsigned int busy_percent; }; /** * struct cache_hwmon - devfreq Cache HW monitor info * @start_hwmon: Start the HW monitoring * @stop_hwmon: Stop the HW monitoring * @meas_mrps_and_set_irq: Return the measured count and set up the * IRQ to fire if usage exceeds current * measurement by @tol percent. * @dev: device that this HW monitor can monitor. * @of_node: OF node of device that this HW monitor can monitor. * @df: Devfreq node that this HW montior is being used * for. NULL when not actively in use, and non-NULL * when in use. */ struct cache_hwmon { int (*start_hwmon)(struct devfreq *df, struct mrps_stats *mrps); void (*stop_hwmon)(struct devfreq *df); bool (*is_valid_irq)(struct devfreq *df); unsigned long (*meas_mrps_and_set_irq)(struct devfreq *df, int (*start_hwmon)(struct cache_hwmon *hw, struct mrps_stats *mrps); void (*stop_hwmon)(struct cache_hwmon *hw); unsigned long (*meas_mrps_and_set_irq)(struct cache_hwmon *hw, unsigned int tol, unsigned int us, struct mrps_stats *mrps); int irq; struct device *dev; struct device_node *of_node; struct devfreq *df; }; #ifdef CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON int register_cache_hwmon(struct cache_hwmon *hwmon); int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon); int update_cache_hwmon(struct cache_hwmon *hwmon); #else static inline int register_cache_hwmon(struct cache_hwmon *hwmon) static inline int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) { return 0; } int update_cache_hwmon(struct cache_hwmon *hwmon) { return 0; } Loading include/trace/events/power.h +43 −0 Original line number Diff line number Diff line Loading @@ -684,6 +684,49 @@ TRACE_EVENT(bw_hwmon_update, __entry->down_thres) ); TRACE_EVENT(cache_hwmon_meas, TP_PROTO(const char *name, unsigned long high_mrps, unsigned long med_mrps, unsigned long low_mrps, unsigned int busy_percent, unsigned int us), TP_ARGS(name, high_mrps, med_mrps, low_mrps, busy_percent, us), TP_STRUCT__entry( __string(name, name) __field(unsigned long, high_mrps) __field(unsigned long, med_mrps) __field(unsigned long, low_mrps) __field(unsigned long, total_mrps) __field(unsigned int, busy_percent) __field(unsigned int, us) ), TP_fast_assign( __assign_str(name, name); __entry->high_mrps = high_mrps; __entry->med_mrps = med_mrps; __entry->low_mrps = low_mrps; __entry->total_mrps = high_mrps + med_mrps + low_mrps; __entry->busy_percent = busy_percent; __entry->us = us; ), TP_printk("dev=%s H=%lu M=%lu L=%lu T=%lu busy_pct=%u period=%u", __get_str(name), __entry->high_mrps, __entry->med_mrps, __entry->low_mrps, __entry->total_mrps, __entry->busy_percent, __entry->us) ); TRACE_EVENT(cache_hwmon_update, TP_PROTO(const char *name, unsigned long freq_mhz), TP_ARGS(name, freq_mhz), TP_STRUCT__entry( __string(name, name) __field(unsigned long, freq) ), TP_fast_assign( __assign_str(name, name); __entry->freq = freq_mhz; ), TP_printk("dev=%s freq=%lu", __get_str(name), __entry->freq) ); #endif /* _TRACE_POWER_H */ /* This part must be outside protection */ Loading Loading
drivers/devfreq/bimc-bwmon.c +48 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/spinlock.h> #include "governor_bw_hwmon.h" Loading @@ -31,11 +32,17 @@ #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) struct bwmon_spec { bool wrap_on_thres; bool overflow; }; struct bwmon { void __iomem *base; void __iomem *global_base; unsigned int mport; unsigned int irq; const struct bwmon_spec *spec; struct device *dev; struct bw_hwmon hw; }; Loading Loading @@ -94,7 +101,7 @@ static void mon_irq_disable(struct bwmon *m) writel_relaxed(val, MON_INT_EN(m)); } static int mon_irq_status(struct bwmon *m) static unsigned int mon_irq_status(struct bwmon *m) { u32 mval; Loading @@ -103,12 +110,12 @@ static int mon_irq_status(struct bwmon *m) dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); return mval & 0x1; return mval; } static void mon_irq_clear(struct bwmon *m) { writel_relaxed(0x1, MON_INT_CLR(m)); writel_relaxed(0x3, MON_INT_CLR(m)); /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); Loading @@ -127,14 +134,22 @@ static u32 mon_get_limit(struct bwmon *m) return readl_relaxed(MON_THRES(m)); } #define THRES_HIT(status) (status & BIT(0)) #define OVERFLOW(status) (status & BIT(1)) static unsigned long mon_get_count(struct bwmon *m) { unsigned long count; unsigned long count, status; count = readl_relaxed(MON_CNT(m)); status = mon_irq_status(m); dev_dbg(m->dev, "Counter: %08lx\n", count); if (mon_irq_status(m)) if (OVERFLOW(status) && m->spec->overflow) count += 0xFFFFFFFF; if (THRES_HIT(status) && m->spec->wrap_on_thres) count += mon_get_limit(m); dev_dbg(m->dev, "Actual Count: %08lx\n", count); return count; Loading Loading @@ -173,11 +188,17 @@ static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw, mbps = mon_get_count(m); mbps = bytes_to_mbps(mbps, us); /* * The fudging of mbps when calculating limit is to workaround a HW * design issue. Needs further tuning. * If the counter wraps on thres, don't set the thres too low. * Setting it too low runs the risk of the counter wrapping around * multiple times before the IRQ is processed. */ if (likely(!m->spec->wrap_on_thres)) limit = mbps_to_bytes(mbps, sample_ms, tol); else limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); mon_set_limit(m, limit); mon_clear(m); Loading Loading @@ -273,11 +294,23 @@ static int resume_bw_hwmon(struct bw_hwmon *hw) /*************************************************************************/ static const struct bwmon_spec spec[] = { { .wrap_on_thres = true, .overflow = false }, { .wrap_on_thres = false, .overflow = true }, }; static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon", .data = &spec[0] }, { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, {} }; static int bimc_bwmon_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct bwmon *m; const struct of_device_id *id; int ret; u32 data; Loading @@ -293,6 +326,13 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) } m->mport = data; id = of_match_device(bimc_bwmon_match_table, dev); if (!id) { dev_err(dev, "Unknown device type!\n"); return -ENODEV; } m->spec = id->data; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); Loading Loading @@ -339,11 +379,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return 0; } static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon" }, {} }; static struct platform_driver bimc_bwmon_driver = { .probe = bimc_bwmon_driver_probe, .driver = { Loading
drivers/devfreq/governor_cache_hwmon.c +170 −79 Original line number Diff line number Diff line Loading @@ -20,14 +20,41 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/devfreq.h> #include <trace/events/power.h> #include "governor.h" #include "governor_cache_hwmon.h" struct cache_hwmon_node { unsigned int cycles_per_low_req; unsigned int cycles_per_med_req; unsigned int cycles_per_high_req; unsigned int min_busy; unsigned int max_busy; unsigned int tolerance_mrps; unsigned int guard_band_mhz; unsigned int decay_rate; unsigned long prev_mhz; ktime_t prev_ts; bool mon_started; struct list_head list; void *orig_data; struct cache_hwmon *hw; struct attribute_group *attr_grp; }; static LIST_HEAD(cache_hwmon_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(state_lock); #define show_attr(name) \ static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ return scnprintf(buf, PAGE_SIZE, "%u\n", name); \ struct devfreq *df = to_devfreq(dev); \ struct cache_hwmon_node *hw = df->data; \ return scnprintf(buf, PAGE_SIZE, "%u\n", hw->name); \ } #define store_attr(name, _min, _max) \ Loading @@ -37,12 +64,14 @@ static ssize_t store_##name(struct device *dev, \ { \ int ret; \ unsigned int val; \ struct devfreq *df = to_devfreq(dev); \ struct cache_hwmon_node *hw = df->data; \ ret = kstrtoint(buf, 10, &val); \ if (ret) \ return ret; \ val = max(val, _min); \ val = min(val, _max); \ name = val; \ hw->name = val; \ return count; \ } Loading @@ -51,28 +80,31 @@ show_attr(__attr) \ store_attr(__attr, (min), (max)) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) static struct cache_hwmon *hw; static unsigned int cycles_per_low_req; static unsigned int cycles_per_med_req = 20; static unsigned int cycles_per_high_req = 35; static unsigned int min_busy = 100; static unsigned int max_busy = 100; static unsigned int tolerance_mrps = 5; static unsigned int guard_band_mhz = 100; static unsigned int decay_rate = 90; #define MIN_MS 10U #define MAX_MS 500U static unsigned int sample_ms = 50; static unsigned long prev_mhz; static ktime_t prev_ts; static unsigned long measure_mrps_and_set_irq(struct devfreq *df, static struct cache_hwmon_node *find_hwmon_node(struct devfreq *df) { struct cache_hwmon_node *node, *found = NULL; mutex_lock(&list_lock); list_for_each_entry(node, &cache_hwmon_list, list) if (node->hw->dev == df->dev.parent || node->hw->of_node == df->dev.parent->of_node) { found = node; break; } mutex_unlock(&list_lock); return found; } static unsigned long measure_mrps_and_set_irq(struct cache_hwmon_node *node, struct mrps_stats *stat) { ktime_t ts; unsigned int us; struct cache_hwmon *hw = node->hw; /* * Since we are stopping the counters, we don't want this short work Loading @@ -84,59 +116,70 @@ static unsigned long measure_mrps_and_set_irq(struct devfreq *df, preempt_disable(); ts = ktime_get(); us = ktime_to_us(ktime_sub(ts, prev_ts)); us = ktime_to_us(ktime_sub(ts, node->prev_ts)); if (!us) us = 1; hw->meas_mrps_and_set_irq(df, tolerance_mrps, us, stat); prev_ts = ts; hw->meas_mrps_and_set_irq(hw, node->tolerance_mrps, us, stat); node->prev_ts = ts; preempt_enable(); pr_debug("stat H=%3lu, M=%3lu, T=%3lu, b=%3u, f=%4lu, us=%d\n", stat->high, stat->med, stat->high + stat->med, stat->busy_percent, df->previous_freq / 1000, us); trace_cache_hwmon_meas(dev_name(hw->df->dev.parent), stat->mrps[HIGH], stat->mrps[MED], stat->mrps[LOW], stat->busy_percent, us); return 0; } static void compute_cache_freq(struct mrps_stats *mrps, unsigned long *freq) static void compute_cache_freq(struct cache_hwmon_node *node, struct mrps_stats *mrps, unsigned long *freq) { unsigned long new_mhz; unsigned int busy; new_mhz = mrps->high * cycles_per_high_req + mrps->med * cycles_per_med_req + mrps->low * cycles_per_low_req; new_mhz = mrps->mrps[HIGH] * node->cycles_per_high_req + mrps->mrps[MED] * node->cycles_per_med_req + mrps->mrps[LOW] * node->cycles_per_low_req; busy = max(min_busy, mrps->busy_percent); busy = min(max_busy, busy); busy = max(node->min_busy, mrps->busy_percent); busy = min(node->max_busy, busy); new_mhz *= 100; new_mhz /= busy; if (new_mhz < prev_mhz) { new_mhz = new_mhz * decay_rate + prev_mhz * (100 - decay_rate); if (new_mhz < node->prev_mhz) { new_mhz = new_mhz * node->decay_rate + node->prev_mhz * (100 - node->decay_rate); new_mhz /= 100; } prev_mhz = new_mhz; node->prev_mhz = new_mhz; new_mhz += guard_band_mhz; new_mhz += node->guard_band_mhz; *freq = new_mhz * 1000; trace_cache_hwmon_update(dev_name(node->hw->df->dev.parent), *freq); } #define TOO_SOON_US (1 * USEC_PER_MSEC) static irqreturn_t mon_intr_handler(int irq, void *dev) int update_cache_hwmon(struct cache_hwmon *hwmon) { struct devfreq *df = dev; struct cache_hwmon_node *node; struct devfreq *df; ktime_t ts; unsigned int us; int ret; if (!hw->is_valid_irq(df)) return IRQ_NONE; pr_debug("Got interrupt\n"); if (!hwmon) return -EINVAL; df = hwmon->df; if (!df) return -ENODEV; node = df->data; if (!node) return -ENODEV; if (!node->mon_started) return -EBUSY; dev_dbg(df->dev.parent, "Got update request\n"); devfreq_monitor_stop(df); /* Loading @@ -152,27 +195,30 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) * readjusted. */ ts = ktime_get(); us = ktime_to_us(ktime_sub(ts, prev_ts)); us = ktime_to_us(ktime_sub(ts, node->prev_ts)); if (us > TOO_SOON_US) { mutex_lock(&df->lock); ret = update_devfreq(df); if (ret) pr_err("Unable to update freq on IRQ!\n"); dev_err(df->dev.parent, "Unable to update freq on request!\n"); mutex_unlock(&df->lock); } devfreq_monitor_start(df); return IRQ_HANDLED; return 0; } static int devfreq_cache_hwmon_get_freq(struct devfreq *df, unsigned long *freq) { struct mrps_stats stat; struct cache_hwmon_node *node = df->data; measure_mrps_and_set_irq(df, &stat); compute_cache_freq(&stat, freq); memset(&stat, 0, sizeof(stat)); measure_mrps_and_set_irq(node, &stat); compute_cache_freq(node, &stat, freq); return 0; } Loading Loading @@ -207,58 +253,73 @@ static int start_monitoring(struct devfreq *df) { int ret; struct mrps_stats mrps; prev_ts = ktime_get(); prev_mhz = 0; mrps.high = (df->previous_freq / 1000) - guard_band_mhz; mrps.high /= cycles_per_high_req; ret = hw->start_hwmon(df, &mrps); struct device *dev = df->dev.parent; struct cache_hwmon_node *node; struct cache_hwmon *hw; node = find_hwmon_node(df); if (!node) { dev_err(dev, "Unable to find HW monitor!\n"); return -ENODEV; } hw = node->hw; hw->df = df; node->orig_data = df->data; df->data = node; node->prev_ts = ktime_get(); node->prev_mhz = 0; mrps.mrps[HIGH] = (df->previous_freq / 1000) - node->guard_band_mhz; mrps.mrps[HIGH] /= node->cycles_per_high_req; mrps.mrps[MED] = mrps.mrps[LOW] = 0; ret = hw->start_hwmon(hw, &mrps); if (ret) { pr_err("Unable to start HW monitor!\n"); return ret; dev_err(dev, "Unable to start HW monitor!\n"); goto err_start; } devfreq_monitor_start(df); ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, "cache_hwmon", df); if (ret) { pr_err("Unable to register interrupt handler!\n"); goto req_irq_fail; } node->mon_started = true; ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) { pr_err("Error creating sys entries!\n"); dev_err(dev, "Error creating sys entries!\n"); goto sysfs_fail; } return 0; sysfs_fail: disable_irq(hw->irq); free_irq(hw->irq, df); req_irq_fail: node->mon_started = false; devfreq_monitor_stop(df); hw->stop_hwmon(df); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; return ret; } static void stop_monitoring(struct devfreq *df) { struct cache_hwmon_node *node = df->data; struct cache_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, &dev_attr_group); disable_irq(hw->irq); free_irq(hw->irq, df); node->mon_started = false; devfreq_monitor_stop(df); hw->stop_hwmon(df); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; } static int devfreq_cache_hwmon_ev_handler(struct devfreq *df, unsigned int event, void *data) { int ret; unsigned int sample_ms; switch (event) { case DEVFREQ_GOV_START: Loading @@ -271,12 +332,12 @@ static int devfreq_cache_hwmon_ev_handler(struct devfreq *df, if (ret) return ret; pr_debug("Enabled Cache HW monitor governor\n"); dev_dbg(df->dev.parent, "Enabled Cache HW monitor governor\n"); break; case DEVFREQ_GOV_STOP: stop_monitoring(df); pr_debug("Disabled Cache HW monitor governor\n"); dev_dbg(df->dev.parent, "Disabled Cache HW monitor governor\n"); break; case DEVFREQ_GOV_INTERVAL: Loading @@ -296,18 +357,48 @@ static struct devfreq_governor devfreq_cache_hwmon = { .event_handler = devfreq_cache_hwmon_ev_handler, }; int register_cache_hwmon(struct cache_hwmon *hwmon) int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) { int ret; hw = hwmon; int ret = 0; struct cache_hwmon_node *node; if (!hwmon->dev && !hwmon->of_node) return -EINVAL; node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; node->cycles_per_med_req = 20; node->cycles_per_high_req = 35; node->min_busy = 100; node->max_busy = 100; node->tolerance_mrps = 5; node->guard_band_mhz = 100; node->decay_rate = 90; node->hw = hwmon; node->attr_grp = &dev_attr_group; mutex_lock(&state_lock); if (!use_cnt) { ret = devfreq_add_governor(&devfreq_cache_hwmon); if (ret) { pr_err("devfreq governor registration failed\n"); if (!ret) use_cnt++; } mutex_unlock(&state_lock); if (!ret) { dev_info(dev, "Cache HWmon governor registered.\n"); } else { dev_err(dev, "Failed to add Cache HWmon governor\n"); return ret; } return 0; mutex_lock(&list_lock); list_add_tail(&node->list, &cache_hwmon_list); mutex_unlock(&list_lock); return ret; } MODULE_DESCRIPTION("HW monitor based cache freq driver"); Loading
drivers/devfreq/governor_cache_hwmon.h +35 −10 Original line number Diff line number Diff line Loading @@ -9,27 +9,52 @@ #include <linux/kernel.h> #include <linux/devfreq.h> enum request_group { HIGH, MED, LOW, MAX_NUM_GROUPS, }; struct mrps_stats { unsigned long high; unsigned long med; unsigned long low; unsigned long mrps[MAX_NUM_GROUPS]; unsigned int busy_percent; }; /** * struct cache_hwmon - devfreq Cache HW monitor info * @start_hwmon: Start the HW monitoring * @stop_hwmon: Stop the HW monitoring * @meas_mrps_and_set_irq: Return the measured count and set up the * IRQ to fire if usage exceeds current * measurement by @tol percent. * @dev: device that this HW monitor can monitor. * @of_node: OF node of device that this HW monitor can monitor. * @df: Devfreq node that this HW montior is being used * for. NULL when not actively in use, and non-NULL * when in use. */ struct cache_hwmon { int (*start_hwmon)(struct devfreq *df, struct mrps_stats *mrps); void (*stop_hwmon)(struct devfreq *df); bool (*is_valid_irq)(struct devfreq *df); unsigned long (*meas_mrps_and_set_irq)(struct devfreq *df, int (*start_hwmon)(struct cache_hwmon *hw, struct mrps_stats *mrps); void (*stop_hwmon)(struct cache_hwmon *hw); unsigned long (*meas_mrps_and_set_irq)(struct cache_hwmon *hw, unsigned int tol, unsigned int us, struct mrps_stats *mrps); int irq; struct device *dev; struct device_node *of_node; struct devfreq *df; }; #ifdef CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON int register_cache_hwmon(struct cache_hwmon *hwmon); int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon); int update_cache_hwmon(struct cache_hwmon *hwmon); #else static inline int register_cache_hwmon(struct cache_hwmon *hwmon) static inline int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) { return 0; } int update_cache_hwmon(struct cache_hwmon *hwmon) { return 0; } Loading
include/trace/events/power.h +43 −0 Original line number Diff line number Diff line Loading @@ -684,6 +684,49 @@ TRACE_EVENT(bw_hwmon_update, __entry->down_thres) ); TRACE_EVENT(cache_hwmon_meas, TP_PROTO(const char *name, unsigned long high_mrps, unsigned long med_mrps, unsigned long low_mrps, unsigned int busy_percent, unsigned int us), TP_ARGS(name, high_mrps, med_mrps, low_mrps, busy_percent, us), TP_STRUCT__entry( __string(name, name) __field(unsigned long, high_mrps) __field(unsigned long, med_mrps) __field(unsigned long, low_mrps) __field(unsigned long, total_mrps) __field(unsigned int, busy_percent) __field(unsigned int, us) ), TP_fast_assign( __assign_str(name, name); __entry->high_mrps = high_mrps; __entry->med_mrps = med_mrps; __entry->low_mrps = low_mrps; __entry->total_mrps = high_mrps + med_mrps + low_mrps; __entry->busy_percent = busy_percent; __entry->us = us; ), TP_printk("dev=%s H=%lu M=%lu L=%lu T=%lu busy_pct=%u period=%u", __get_str(name), __entry->high_mrps, __entry->med_mrps, __entry->low_mrps, __entry->total_mrps, __entry->busy_percent, __entry->us) ); TRACE_EVENT(cache_hwmon_update, TP_PROTO(const char *name, unsigned long freq_mhz), TP_ARGS(name, freq_mhz), TP_STRUCT__entry( __string(name, name) __field(unsigned long, freq) ), TP_fast_assign( __assign_str(name, name); __entry->freq = freq_mhz; ), TP_printk("dev=%s freq=%lu", __get_str(name), __entry->freq) ); #endif /* _TRACE_POWER_H */ /* This part must be outside protection */ Loading