diff --git a/Documentation/devicetree/bindings/arm/msm/kraitbw-l2pm.txt b/Documentation/devicetree/bindings/arm/msm/kraitbw-l2pm.txt new file mode 100644 index 000000000000..ccfebc79df89 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/kraitbw-l2pm.txt @@ -0,0 +1,20 @@ +MSM Krait L2 performance monitor counters for bandwidth measurement device + +krait-l2pm is a device that represents the Krait L2 PM counters that can be +used to measure the bandwidth of read/write traffic from the Krait CPU +subsystem. + +Required properties: +- compatible: Must be "qcom,kraitbw-l2pm" +- interrupts: Lists the L2 PM counter overflow IRQ. +- qcom,bytes-per-beat: The number of bytes transferred in one data beat from + the Krait CPU subsystem. +- qcom,target-dev: Target device for cache scaling + +Example: + qcom,kraitbw-l2pm { + compatible = "qcom,kraitbw-l2pm"; + interrupts = <0 1 1>; + qcom,bytes-per-beat = <8>; + qcom,target-dev = <&cache>; + }; diff --git a/drivers/devfreq/governor_cache_hwmon.c b/drivers/devfreq/governor_cache_hwmon.c index 3fff31aff6c7..86fa32b2d6bf 100644 --- a/drivers/devfreq/governor_cache_hwmon.c +++ b/drivers/devfreq/governor_cache_hwmon.c @@ -31,11 +31,36 @@ #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; + 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 snprintf(buf, PAGE_SIZE, "%u\n", name); \ + struct devfreq *df = to_devfreq(dev); \ + struct cache_hwmon_node *hw = df->data; \ + return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \ } #define store_attr(name, _min, _max) \ @@ -45,12 +70,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 = sscanf(buf, "%u", &val); \ if (ret != 1) \ return -EINVAL; \ val = max(val, _min); \ val = min(val, _max); \ - name = val; \ + hw->name = val; \ return count; \ } @@ -59,28 +86,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 @@ -92,59 +122,63 @@ 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", + dev_dbg(hw->df->dev.parent, + "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); + stat->busy_percent, hw->df->previous_freq / 1000, 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->high * node->cycles_per_high_req + + mrps->med * node->cycles_per_med_req + + 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; } #define TOO_SOON_US (1 * USEC_PER_MSEC) static irqreturn_t mon_intr_handler(int irq, void *dev) { - struct devfreq *df = dev; + struct cache_hwmon_node *node = dev; + struct devfreq *df = node->hw->df; ktime_t ts; unsigned int us; int ret; - if (!hw->is_valid_irq(df)) + if (!node->hw->is_valid_irq(node->hw)) return IRQ_NONE; - pr_debug("Got interrupt\n"); + dev_dbg(df->dev.parent, "Got interrupt\n"); devfreq_monitor_stop(df); /* @@ -160,12 +194,13 @@ 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 IRQ!\n"); mutex_unlock(&df->lock); } @@ -179,9 +214,11 @@ static int devfreq_cache_hwmon_get_freq(struct devfreq *df, u32 *flag) { 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; } @@ -216,58 +253,88 @@ static int start_monitoring(struct devfreq *df) { int ret; struct mrps_stats mrps; + struct device *dev = df->dev.parent; + struct cache_hwmon_node *node; + struct cache_hwmon *hw; - prev_ts = ktime_get(); - prev_mhz = 0; - mrps.high = (df->previous_freq / 1000) - guard_band_mhz; - mrps.high /= cycles_per_high_req; + 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; - ret = hw->start_hwmon(df, &mrps); + node->prev_ts = ktime_get(); + node->prev_mhz = 0; + mrps.high = (df->previous_freq / 1000) - node->guard_band_mhz; + mrps.high /= node->cycles_per_high_req; + mrps.med = 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, + if (hw->irq) + ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, - "cache_hwmon", df); + "cache_hwmon", node); if (ret) { - pr_err("Unable to register interrupt handler!\n"); + dev_err(dev, "Unable to register interrupt handler!\n"); goto req_irq_fail; } 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); + if (hw->irq) { + disable_irq(hw->irq); + free_irq(hw->irq, node); + } req_irq_fail: 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); + if (hw->irq) { + disable_irq(hw->irq); + free_irq(hw->irq, node); + } 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: @@ -280,12 +347,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: @@ -305,18 +372,50 @@ 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; + int ret = 0; + struct cache_hwmon_node *node; - hw = hwmon; - ret = devfreq_add_governor(&devfreq_cache_hwmon); - if (ret) { - pr_err("devfreq governor registration failed\n"); + if (!hwmon->dev && !hwmon->of_node) + return -EINVAL; + + node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); + if (!node) { + dev_err(dev, "Unable to register gov. Out of memory!\n"); + 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) + 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"); diff --git a/drivers/devfreq/governor_cache_hwmon.h b/drivers/devfreq/governor_cache_hwmon.h index 5361894e201a..21a1eefc64ab 100644 --- a/drivers/devfreq/governor_cache_hwmon.h +++ b/drivers/devfreq/governor_cache_hwmon.h @@ -24,20 +24,40 @@ struct mrps_stats { unsigned int busy_percent; }; +/** + * struct cache_hwmon - devfreq Cache HW monitor info + * @start_hwmon: Start the HW monitoring + * @stop_hwmon: Stop the HW monitoring + * @is_valid_irq: Check whether the IRQ was triggered by the counter + * used to monitor cache activity. + * @meas_mrps_and_set_irq: Return the measured count and set up the + * IRQ to fire if usage exceeds current + * measurement by @tol percent. + * @irq: IRQ number that corresponds to this HW monitor. + * @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); + bool (*is_valid_irq)(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_MSM_CACHE_HWMON -int register_cache_hwmon(struct cache_hwmon *hwmon); +int register_cache_hwmon(struct device *dev, 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; } diff --git a/drivers/devfreq/krait-l2pm.c b/drivers/devfreq/krait-l2pm.c index 3772786950b7..09d770bfdb44 100644 --- a/drivers/devfreq/krait-l2pm.c +++ b/drivers/devfreq/krait-l2pm.c @@ -298,13 +298,13 @@ static unsigned int mrps_to_count(unsigned int mrps, unsigned int ms, return mrps; } -static unsigned long meas_mrps_and_set_irq(struct devfreq *df, +static unsigned long meas_mrps_and_set_irq(struct cache_hwmon *hw, unsigned int tol, unsigned int us, struct mrps_stats *mrps) { u32 limit; - unsigned int sample_ms = df->profile->polling_ms; - unsigned long f = df->previous_freq; + unsigned int sample_ms = hw->df->profile->polling_ms; + unsigned long f = hw->df->previous_freq; unsigned long t_mrps, m_mrps, l2_cyc; mon_disable(L2_H_REQ_MON); @@ -335,12 +335,12 @@ static unsigned long meas_mrps_and_set_irq(struct devfreq *df, return 0; } -static bool is_valid_mrps_irq(struct devfreq *df) +static bool is_valid_mrps_irq(struct cache_hwmon *hw) { return mon_overflow(L2_H_REQ_MON) || mon_overflow(L2_M_REQ_MON); } -static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps) +static int start_mrps_hwmon(struct cache_hwmon *hw, struct mrps_stats *mrps) { u32 limit; @@ -349,7 +349,7 @@ static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps) mon_disable(L2_M_REQ_MON); mon_disable(L2_CYC_MON); - limit = mrps_to_count(mrps->high, df->profile->polling_ms, 0); + limit = mrps_to_count(mrps->high, hw->df->profile->polling_ms, 0); prev_req_start_val = mon_set_limit(L2_H_REQ_MON, limit); mon_set_limit(L2_M_REQ_MON, 0xFFFFFFFF); mon_set_limit(L2_CYC_MON, 0xFFFFFFFF); @@ -364,7 +364,7 @@ static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps) return 0; } -static void stop_mrps_hwmon(struct devfreq *df) +static void stop_mrps_hwmon(struct cache_hwmon *hw) { global_mon_enable(false); mon_disable(L2_H_REQ_MON); @@ -393,7 +393,6 @@ static int krait_l2pm_driver_probe(struct platform_device *pdev) pr_err("Unable to get IRQ number\n"); return bw_irq; } - mrps_hwmon.irq = bw_irq; ret = of_property_read_u32(dev->of_node, "qcom,bytes-per-beat", &bytes_per_beat); @@ -406,7 +405,13 @@ static int krait_l2pm_driver_probe(struct platform_device *pdev) if (ret) pr_err("CPUBW hwmon registration failed\n"); - ret2 = register_cache_hwmon(&mrps_hwmon); + mrps_hwmon.irq = bw_irq; + mrps_hwmon.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", + 0); + if (!mrps_hwmon.of_node) + return -EINVAL; + + ret2 = register_cache_hwmon(dev, &mrps_hwmon); if (ret2) pr_err("Cache hwmon registration failed\n");