Loading arch/arm/mach-msm/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -272,7 +272,7 @@ config MSM_KRAIT_TBB_ABORT_HANDLER config ARCH_MSM_KRAIT bool select ARM_L1_CACHE_SHIFT_6 select DEVFREQ_GOV_MSM_CPUBW_HWMON select DEVFREQ_GOV_MSM_BW_HWMON select DEVFREQ_GOV_MSM_CACHE_HWMON config MSM_CORTEX_A7 Loading drivers/devfreq/Kconfig +9 −8 Original line number Diff line number Diff line Loading @@ -79,16 +79,17 @@ config DEVFREQ_GOV_MSM_ADRENO_TZ Sets the frequency using a "on-demand" algorithm. This governor is unlikely to be useful for other devices. config DEVFREQ_GOV_MSM_CPUBW_HWMON tristate "HW monitor based governor for CPUBW" config DEVFREQ_GOV_MSM_BW_HWMON tristate "HW monitor based governor for device BW" depends on ARCH_MSM_KRAIT help HW monitor based governor for CPU to DDR bandwidth voting. This governor currently supports only Krait L2 PM counters. Sets the CPU BW vote by using L2 PM counters to monitor the Krait's use of DDR. Since this governor uses some of the PM counters it can conflict with existing profiling tools. This governor is unlikely to be useful for other devices. HW monitor based governor for device to DDR bandwidth voting. When this governor is used for Krait CPUs, it sets the Krait CPU BW vote by using L2 PM counters to monitor the Krait's use of DDR. Since this uses some of the Krait PM counters it can conflict with existing profiling tools. This governor is unlikely to be useful for non-MSM devices. config DEVFREQ_GOV_MSM_CACHE_HWMON tristate "HW monitor based governor for cache frequency" Loading drivers/devfreq/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ) += governor_cpufreq.o obj-$(CONFIG_DEVFREQ_GOV_MSM_ADRENO_TZ) += governor_msm_adreno_tz.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ) += governor_msm_cpufreq.o obj-$(CONFIG_ARCH_MSM_KRAIT) += krait-l2pm.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUBW_HWMON) += governor_cpubw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_BW_HWMON) += governor_bw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CACHE_HWMON) += governor_cache_hwmon.o # DEVFREQ Drivers Loading drivers/devfreq/governor_cpubw_hwmon.c→drivers/devfreq/governor_bw_hwmon.c +437 −0 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ * GNU General Public License for more details. */ #define pr_fmt(fmt) "cpubw-hwmon: " fmt #define pr_fmt(fmt) "bw-hwmon: " fmt #include <linux/kernel.h> #include <linux/sizes.h> Loading @@ -29,13 +29,37 @@ #include <linux/of.h> #include <linux/devfreq.h> #include "governor.h" #include "governor_cpubw_hwmon.h" #include "governor_bw_hwmon.h" struct hwmon_node { unsigned int tolerance_percent; unsigned int guard_band_mbps; unsigned int decay_rate; unsigned int io_percent; unsigned int bw_step; unsigned long prev_ab; unsigned long *dev_ab; ktime_t prev_ts; struct list_head list; void *orig_data; struct bw_hwmon *hw; struct devfreq_governor *gov; struct attribute_group *attr_grp; }; static LIST_HEAD(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 sprintf(buf, "%u\n", name); \ struct devfreq *df = to_devfreq(dev); \ struct hwmon_node *hw = df->data; \ return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \ } #define store_attr(name, _min, _max) \ Loading @@ -43,6 +67,8 @@ static ssize_t store_##name(struct device *dev, \ struct device_attribute *attr, const char *buf, \ size_t count) \ { \ struct devfreq *df = to_devfreq(dev); \ struct hwmon_node *hw = df->data; \ int ret; \ unsigned int val; \ ret = sscanf(buf, "%u", &val); \ Loading @@ -50,7 +76,7 @@ static ssize_t store_##name(struct device *dev, \ return -EINVAL; \ val = max(val, _min); \ val = min(val, _max); \ name = val; \ hw->name = val; \ return count; \ } Loading @@ -59,25 +85,15 @@ show_attr(__attr) \ store_attr(__attr, min, max) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) static struct cpubw_hwmon *hw; static unsigned int tolerance_percent = 10; static unsigned int guard_band_mbps = 100; static unsigned int decay_rate = 90; static unsigned int io_percent = 16; static unsigned int bw_step = 190; #define MIN_MS 10U #define MAX_MS 500U static unsigned int sample_ms = 50; static unsigned long prev_ab; static ktime_t prev_ts; static unsigned long measure_bw_and_set_irq(struct devfreq *df) static unsigned long measure_bw_and_set_irq(struct hwmon_node *node) { ktime_t ts; unsigned int us; unsigned long mbps; struct bw_hwmon *hw = node->hw; /* * Since we are stopping the counters, we don't want this short work Loading @@ -89,50 +105,54 @@ static unsigned long measure_bw_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; mbps = hw->meas_bw_and_set_irq(df, tolerance_percent, us); prev_ts = ts; mbps = hw->meas_bw_and_set_irq(hw, node->tolerance_percent, us); node->prev_ts = ts; preempt_enable(); pr_debug("BW MBps = %6lu, period = %u\n", mbps, us); dev_dbg(hw->df->dev.parent, "BW MBps = %6lu, period = %u\n", mbps, us); return mbps; } static void compute_bw(int mbps, unsigned long *freq, unsigned long *ab) static void compute_bw(struct hwmon_node *node, int mbps, unsigned long *freq, unsigned long *ab) { int new_bw; mbps += guard_band_mbps; mbps += node->guard_band_mbps; if (mbps > prev_ab) { if (mbps > node->prev_ab) { new_bw = mbps; } else { new_bw = mbps * decay_rate + prev_ab * (100 - decay_rate); new_bw = mbps * node->decay_rate + node->prev_ab * (100 - node->decay_rate); new_bw /= 100; } prev_ab = new_bw; *ab = roundup(new_bw, bw_step); *freq = (new_bw * 100) / io_percent; node->prev_ab = new_bw; if (ab) *ab = roundup(new_bw, node->bw_step); *freq = (new_bw * 100) / node->io_percent; } #define TOO_SOON_US (1 * USEC_PER_MSEC) static irqreturn_t mon_intr_handler(int irq, void *dev) { struct devfreq *df = dev; struct 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); /* Loading @@ -146,12 +166,13 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) * estimate is not very off and doesn't need to be 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); } Loading @@ -160,49 +181,128 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) return IRQ_HANDLED; } static struct hwmon_node *find_hwmon_node(struct devfreq *df) { struct hwmon_node *node, *found = NULL; mutex_lock(&list_lock); list_for_each_entry(node, &hwmon_list, list) if (node->hw->dev == df->dev.parent || node->hw->of_node == df->dev.parent->of_node || node->gov == df->governor) { found = node; break; } mutex_unlock(&list_lock); return found; } static int start_monitoring(struct devfreq *df) { int ret; int ret = 0; unsigned long mbps; struct device *dev = df->dev.parent; struct hwmon_node *node; struct bw_hwmon *hw; struct devfreq_dev_status stat; node = find_hwmon_node(df); if (!node) { dev_err(dev, "Unable to find HW monitor!\n"); return -ENODEV; } hw = node->hw; stat.private_data = NULL; if (df->profile->get_dev_status) ret = df->profile->get_dev_status(df->dev.parent, &stat); if (ret || !stat.private_data) dev_warn(dev, "Device doesn't take AB votes!\n"); else node->dev_ab = stat.private_data; hw->df = df; node->orig_data = df->data; df->data = node; node->prev_ts = ktime_get(); node->prev_ab = 0; mbps = (df->previous_freq * node->io_percent) / 100; ret = hw->start_hwmon(hw, mbps); if (ret) { dev_err(dev, "Unable to start HW monitor!\n"); goto err_start; } devfreq_monitor_start(df); if (hw->irq) ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, "cpubw_hwmon", df); "bw_hwmon", node); if (ret) { pr_err("Unable to register interrupt handler!\n"); return ret; dev_err(dev, "Unable to register interrupt handler!\n"); goto err_req_irq; } prev_ts = ktime_get(); prev_ab = 0; ret = sysfs_create_group(&df->dev.kobj, node->attr_grp); if (ret) goto err_sysfs; mbps = (df->previous_freq * io_percent) / 100; return 0; ret = hw->start_hwmon(df, mbps); if (ret) { pr_err("Unable to start HW monitor!\n"); free_irq(hw->irq, df); return ret; err_sysfs: if (hw->irq) { disable_irq(hw->irq); free_irq(hw->irq, node); } return 0; err_req_irq: devfreq_monitor_stop(df); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; node->dev_ab = NULL; return ret; } static void stop_monitoring(struct devfreq *df) { hw->stop_hwmon(df); struct hwmon_node *node = df->data; struct bw_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, node->attr_grp); if (hw->irq) { disable_irq(hw->irq); free_irq(hw->irq, df); free_irq(hw->irq, node); } devfreq_monitor_stop(df); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; /* * Not all governors know about this additional extended device * configuration. To avoid leaving the extended configuration at a * stale state, set it to 0 and let the next governor take it from * there. */ if (node->dev_ab) *node->dev_ab = 0; node->dev_ab = NULL; } static int devfreq_cpubw_hwmon_get_freq(struct devfreq *df, static int devfreq_bw_hwmon_get_freq(struct devfreq *df, unsigned long *freq, u32 *flag) { unsigned long mbps; struct hwmon_node *node = df->data; mbps = measure_bw_and_set_irq(df); compute_bw(mbps, freq, df->data); mbps = measure_bw_and_set_irq(node); compute_bw(node, mbps, freq, node->dev_ab); return 0; } Loading @@ -223,39 +323,35 @@ static struct attribute *dev_attr[] = { }; static struct attribute_group dev_attr_group = { .name = "cpubw_hwmon", .name = "bw_hwmon", .attrs = dev_attr, }; static int devfreq_cpubw_hwmon_ev_handler(struct devfreq *df, static int devfreq_bw_hwmon_ev_handler(struct devfreq *df, unsigned int event, void *data) { int ret; unsigned int sample_ms; switch (event) { case DEVFREQ_GOV_START: ret = start_monitoring(df); if (ret) return ret; ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) return ret; sample_ms = df->profile->polling_ms; sample_ms = max(MIN_MS, sample_ms); sample_ms = min(MAX_MS, sample_ms); df->profile->polling_ms = sample_ms; devfreq_monitor_start(df); pr_debug("Enabled CPU BW HW monitor governor\n"); ret = start_monitoring(df); if (ret) return ret; dev_dbg(df->dev.parent, "Enabled dev BW HW monitor governor\n"); break; case DEVFREQ_GOV_STOP: sysfs_remove_group(&df->dev.kobj, &dev_attr_group); devfreq_monitor_stop(df); *(unsigned long *)df->data = 0; stop_monitoring(df); pr_debug("Disabled CPU BW HW monitor governor\n"); dev_dbg(df->dev.parent, "Disabled dev BW HW monitor governor\n"); break; case DEVFREQ_GOV_INTERVAL: Loading @@ -269,31 +365,73 @@ static int devfreq_cpubw_hwmon_ev_handler(struct devfreq *df, return 0; } static struct devfreq_governor devfreq_cpubw_hwmon = { .name = "cpubw_hwmon", .get_target_freq = devfreq_cpubw_hwmon_get_freq, .event_handler = devfreq_cpubw_hwmon_ev_handler, static struct devfreq_governor devfreq_gov_bw_hwmon = { .name = "bw_hwmon", .get_target_freq = devfreq_bw_hwmon_get_freq, .event_handler = devfreq_bw_hwmon_ev_handler, }; int register_cpubw_hwmon(struct cpubw_hwmon *hwmon) int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon) { int ret; int ret = 0; struct hwmon_node *node; struct attribute_group *attr_grp; if (!hwmon->gov && !hwmon->dev && !hwmon->of_node) return -EINVAL; if (hw != NULL) { pr_err("cpubw hwmon already registered\n"); return -EBUSY; node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) { dev_err(dev, "Unable to register gov. Out of memory!\n"); return -ENOMEM; } hw = hwmon; if (hwmon->gov) { attr_grp = devm_kzalloc(dev, sizeof(*attr_grp), GFP_KERNEL); if (!attr_grp) return -ENOMEM; ret = devfreq_add_governor(&devfreq_cpubw_hwmon); if (ret) { pr_err("devfreq governor registration failed\n"); return ret; hwmon->gov->get_target_freq = devfreq_bw_hwmon_get_freq; hwmon->gov->event_handler = devfreq_bw_hwmon_ev_handler; attr_grp->name = hwmon->gov->name; attr_grp->attrs = dev_attr; node->gov = hwmon->gov; node->attr_grp = attr_grp; } else { node->gov = &devfreq_gov_bw_hwmon; node->attr_grp = &dev_attr_group; } return 0; node->tolerance_percent = 10; node->guard_band_mbps = 100; node->decay_rate = 90; node->io_percent = 16; node->bw_step = 190; node->hw = hwmon; mutex_lock(&list_lock); list_add_tail(&node->list, &hwmon_list); mutex_unlock(&list_lock); if (hwmon->gov) { ret = devfreq_add_governor(hwmon->gov); } else { mutex_lock(&state_lock); if (!use_cnt) ret = devfreq_add_governor(&devfreq_gov_bw_hwmon); if (!ret) use_cnt++; mutex_unlock(&state_lock); } if (!ret) dev_info(dev, "BW HWmon governor registered.\n"); else dev_err(dev, "BW HWmon governor registration failed!\n"); return ret; } MODULE_DESCRIPTION("HW monitor based CPU DDR bandwidth voting driver"); MODULE_DESCRIPTION("HW monitor based dev DDR bandwidth voting driver"); MODULE_LICENSE("GPL v2"); drivers/devfreq/governor_cpubw_hwmon.h→drivers/devfreq/governor_bw_hwmon.h +71 −0 Original line number Diff line number Diff line Loading @@ -11,38 +11,61 @@ * GNU General Public License for more details. */ #ifndef _GOVERNOR_CPUBW_HWMON_H #define _GOVERNOR_CPUBW_HWMON_H #ifndef _GOVERNOR_BW_HWMON_H #define _GOVERNOR_BW_HWMON_H #include <linux/kernel.h> #include <linux/devfreq.h> /** * struct cpubw_hwmon - CPU BW HW monitor ops * @start_hwmon: Start the HW monitoring of the CPU BW * @stop_hwmon: Stop the HW monitoring of CPU BW * struct bw_hwmon - dev BW HW monitor info * @start_hwmon: Start the HW monitoring of the dev BW * @stop_hwmon: Stop the HW monitoring of dev BW * @is_valid_irq: Check whether the IRQ was triggered by the * counters used to monitor CPU BW. * counters used to monitor dev BW. * @meas_bw_and_set_irq: Return the measured bandwidth and set up the * IRQ to fire if the usage exceeds current * measurement by @tol percent. * @irq: IRQ number that corresponds to this HW * monitor. * @dev: Pointer to device that this HW monitor can * monitor. * @of_node: OF node of device that this HW monitor can * monitor. * @gov: devfreq_governor struct that should be used * when registering this HW monitor with devfreq. * Only the name field is expected to be * initialized. * @df: Devfreq node that this HW monitor is being * used for. NULL when not actively in use and * non-NULL when in use. * * One of dev, of_node or governor_name needs to be specified for a * successful registration. * */ struct cpubw_hwmon { int (*start_hwmon)(struct devfreq *df, unsigned long mbps); void (*stop_hwmon)(struct devfreq *df); bool (*is_valid_irq)(struct devfreq *df); unsigned long (*meas_bw_and_set_irq)(struct devfreq *df, struct bw_hwmon { int (*start_hwmon)(struct bw_hwmon *hw, unsigned long mbps); void (*stop_hwmon)(struct bw_hwmon *hw); bool (*is_valid_irq)(struct bw_hwmon *hw); unsigned long (*meas_bw_and_set_irq)(struct bw_hwmon *hw, unsigned int tol, unsigned int us); int irq; struct device *dev; struct device_node *of_node; struct devfreq_governor *gov; struct devfreq *df; }; #ifdef CONFIG_DEVFREQ_GOV_MSM_CPUBW_HWMON int register_cpubw_hwmon(struct cpubw_hwmon *hwmon); #ifdef CONFIG_DEVFREQ_GOV_MSM_BW_HWMON int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon); #else static inline int register_cpubw_hwmon(struct cpubw_hwmon *hwmon) static inline int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon) { return 0; } #endif #endif /* _GOVERNOR_CPUBW_HWMON_H */ #endif /* _GOVERNOR_BW_HWMON_H */ Loading
arch/arm/mach-msm/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -272,7 +272,7 @@ config MSM_KRAIT_TBB_ABORT_HANDLER config ARCH_MSM_KRAIT bool select ARM_L1_CACHE_SHIFT_6 select DEVFREQ_GOV_MSM_CPUBW_HWMON select DEVFREQ_GOV_MSM_BW_HWMON select DEVFREQ_GOV_MSM_CACHE_HWMON config MSM_CORTEX_A7 Loading
drivers/devfreq/Kconfig +9 −8 Original line number Diff line number Diff line Loading @@ -79,16 +79,17 @@ config DEVFREQ_GOV_MSM_ADRENO_TZ Sets the frequency using a "on-demand" algorithm. This governor is unlikely to be useful for other devices. config DEVFREQ_GOV_MSM_CPUBW_HWMON tristate "HW monitor based governor for CPUBW" config DEVFREQ_GOV_MSM_BW_HWMON tristate "HW monitor based governor for device BW" depends on ARCH_MSM_KRAIT help HW monitor based governor for CPU to DDR bandwidth voting. This governor currently supports only Krait L2 PM counters. Sets the CPU BW vote by using L2 PM counters to monitor the Krait's use of DDR. Since this governor uses some of the PM counters it can conflict with existing profiling tools. This governor is unlikely to be useful for other devices. HW monitor based governor for device to DDR bandwidth voting. When this governor is used for Krait CPUs, it sets the Krait CPU BW vote by using L2 PM counters to monitor the Krait's use of DDR. Since this uses some of the Krait PM counters it can conflict with existing profiling tools. This governor is unlikely to be useful for non-MSM devices. config DEVFREQ_GOV_MSM_CACHE_HWMON tristate "HW monitor based governor for cache frequency" Loading
drivers/devfreq/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ) += governor_cpufreq.o obj-$(CONFIG_DEVFREQ_GOV_MSM_ADRENO_TZ) += governor_msm_adreno_tz.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ) += governor_msm_cpufreq.o obj-$(CONFIG_ARCH_MSM_KRAIT) += krait-l2pm.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUBW_HWMON) += governor_cpubw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_BW_HWMON) += governor_bw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CACHE_HWMON) += governor_cache_hwmon.o # DEVFREQ Drivers Loading
drivers/devfreq/governor_cpubw_hwmon.c→drivers/devfreq/governor_bw_hwmon.c +437 −0 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ * GNU General Public License for more details. */ #define pr_fmt(fmt) "cpubw-hwmon: " fmt #define pr_fmt(fmt) "bw-hwmon: " fmt #include <linux/kernel.h> #include <linux/sizes.h> Loading @@ -29,13 +29,37 @@ #include <linux/of.h> #include <linux/devfreq.h> #include "governor.h" #include "governor_cpubw_hwmon.h" #include "governor_bw_hwmon.h" struct hwmon_node { unsigned int tolerance_percent; unsigned int guard_band_mbps; unsigned int decay_rate; unsigned int io_percent; unsigned int bw_step; unsigned long prev_ab; unsigned long *dev_ab; ktime_t prev_ts; struct list_head list; void *orig_data; struct bw_hwmon *hw; struct devfreq_governor *gov; struct attribute_group *attr_grp; }; static LIST_HEAD(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 sprintf(buf, "%u\n", name); \ struct devfreq *df = to_devfreq(dev); \ struct hwmon_node *hw = df->data; \ return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \ } #define store_attr(name, _min, _max) \ Loading @@ -43,6 +67,8 @@ static ssize_t store_##name(struct device *dev, \ struct device_attribute *attr, const char *buf, \ size_t count) \ { \ struct devfreq *df = to_devfreq(dev); \ struct hwmon_node *hw = df->data; \ int ret; \ unsigned int val; \ ret = sscanf(buf, "%u", &val); \ Loading @@ -50,7 +76,7 @@ static ssize_t store_##name(struct device *dev, \ return -EINVAL; \ val = max(val, _min); \ val = min(val, _max); \ name = val; \ hw->name = val; \ return count; \ } Loading @@ -59,25 +85,15 @@ show_attr(__attr) \ store_attr(__attr, min, max) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) static struct cpubw_hwmon *hw; static unsigned int tolerance_percent = 10; static unsigned int guard_band_mbps = 100; static unsigned int decay_rate = 90; static unsigned int io_percent = 16; static unsigned int bw_step = 190; #define MIN_MS 10U #define MAX_MS 500U static unsigned int sample_ms = 50; static unsigned long prev_ab; static ktime_t prev_ts; static unsigned long measure_bw_and_set_irq(struct devfreq *df) static unsigned long measure_bw_and_set_irq(struct hwmon_node *node) { ktime_t ts; unsigned int us; unsigned long mbps; struct bw_hwmon *hw = node->hw; /* * Since we are stopping the counters, we don't want this short work Loading @@ -89,50 +105,54 @@ static unsigned long measure_bw_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; mbps = hw->meas_bw_and_set_irq(df, tolerance_percent, us); prev_ts = ts; mbps = hw->meas_bw_and_set_irq(hw, node->tolerance_percent, us); node->prev_ts = ts; preempt_enable(); pr_debug("BW MBps = %6lu, period = %u\n", mbps, us); dev_dbg(hw->df->dev.parent, "BW MBps = %6lu, period = %u\n", mbps, us); return mbps; } static void compute_bw(int mbps, unsigned long *freq, unsigned long *ab) static void compute_bw(struct hwmon_node *node, int mbps, unsigned long *freq, unsigned long *ab) { int new_bw; mbps += guard_band_mbps; mbps += node->guard_band_mbps; if (mbps > prev_ab) { if (mbps > node->prev_ab) { new_bw = mbps; } else { new_bw = mbps * decay_rate + prev_ab * (100 - decay_rate); new_bw = mbps * node->decay_rate + node->prev_ab * (100 - node->decay_rate); new_bw /= 100; } prev_ab = new_bw; *ab = roundup(new_bw, bw_step); *freq = (new_bw * 100) / io_percent; node->prev_ab = new_bw; if (ab) *ab = roundup(new_bw, node->bw_step); *freq = (new_bw * 100) / node->io_percent; } #define TOO_SOON_US (1 * USEC_PER_MSEC) static irqreturn_t mon_intr_handler(int irq, void *dev) { struct devfreq *df = dev; struct 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); /* Loading @@ -146,12 +166,13 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) * estimate is not very off and doesn't need to be 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); } Loading @@ -160,49 +181,128 @@ static irqreturn_t mon_intr_handler(int irq, void *dev) return IRQ_HANDLED; } static struct hwmon_node *find_hwmon_node(struct devfreq *df) { struct hwmon_node *node, *found = NULL; mutex_lock(&list_lock); list_for_each_entry(node, &hwmon_list, list) if (node->hw->dev == df->dev.parent || node->hw->of_node == df->dev.parent->of_node || node->gov == df->governor) { found = node; break; } mutex_unlock(&list_lock); return found; } static int start_monitoring(struct devfreq *df) { int ret; int ret = 0; unsigned long mbps; struct device *dev = df->dev.parent; struct hwmon_node *node; struct bw_hwmon *hw; struct devfreq_dev_status stat; node = find_hwmon_node(df); if (!node) { dev_err(dev, "Unable to find HW monitor!\n"); return -ENODEV; } hw = node->hw; stat.private_data = NULL; if (df->profile->get_dev_status) ret = df->profile->get_dev_status(df->dev.parent, &stat); if (ret || !stat.private_data) dev_warn(dev, "Device doesn't take AB votes!\n"); else node->dev_ab = stat.private_data; hw->df = df; node->orig_data = df->data; df->data = node; node->prev_ts = ktime_get(); node->prev_ab = 0; mbps = (df->previous_freq * node->io_percent) / 100; ret = hw->start_hwmon(hw, mbps); if (ret) { dev_err(dev, "Unable to start HW monitor!\n"); goto err_start; } devfreq_monitor_start(df); if (hw->irq) ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, "cpubw_hwmon", df); "bw_hwmon", node); if (ret) { pr_err("Unable to register interrupt handler!\n"); return ret; dev_err(dev, "Unable to register interrupt handler!\n"); goto err_req_irq; } prev_ts = ktime_get(); prev_ab = 0; ret = sysfs_create_group(&df->dev.kobj, node->attr_grp); if (ret) goto err_sysfs; mbps = (df->previous_freq * io_percent) / 100; return 0; ret = hw->start_hwmon(df, mbps); if (ret) { pr_err("Unable to start HW monitor!\n"); free_irq(hw->irq, df); return ret; err_sysfs: if (hw->irq) { disable_irq(hw->irq); free_irq(hw->irq, node); } return 0; err_req_irq: devfreq_monitor_stop(df); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; node->dev_ab = NULL; return ret; } static void stop_monitoring(struct devfreq *df) { hw->stop_hwmon(df); struct hwmon_node *node = df->data; struct bw_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, node->attr_grp); if (hw->irq) { disable_irq(hw->irq); free_irq(hw->irq, df); free_irq(hw->irq, node); } devfreq_monitor_stop(df); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; hw->df = NULL; /* * Not all governors know about this additional extended device * configuration. To avoid leaving the extended configuration at a * stale state, set it to 0 and let the next governor take it from * there. */ if (node->dev_ab) *node->dev_ab = 0; node->dev_ab = NULL; } static int devfreq_cpubw_hwmon_get_freq(struct devfreq *df, static int devfreq_bw_hwmon_get_freq(struct devfreq *df, unsigned long *freq, u32 *flag) { unsigned long mbps; struct hwmon_node *node = df->data; mbps = measure_bw_and_set_irq(df); compute_bw(mbps, freq, df->data); mbps = measure_bw_and_set_irq(node); compute_bw(node, mbps, freq, node->dev_ab); return 0; } Loading @@ -223,39 +323,35 @@ static struct attribute *dev_attr[] = { }; static struct attribute_group dev_attr_group = { .name = "cpubw_hwmon", .name = "bw_hwmon", .attrs = dev_attr, }; static int devfreq_cpubw_hwmon_ev_handler(struct devfreq *df, static int devfreq_bw_hwmon_ev_handler(struct devfreq *df, unsigned int event, void *data) { int ret; unsigned int sample_ms; switch (event) { case DEVFREQ_GOV_START: ret = start_monitoring(df); if (ret) return ret; ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) return ret; sample_ms = df->profile->polling_ms; sample_ms = max(MIN_MS, sample_ms); sample_ms = min(MAX_MS, sample_ms); df->profile->polling_ms = sample_ms; devfreq_monitor_start(df); pr_debug("Enabled CPU BW HW monitor governor\n"); ret = start_monitoring(df); if (ret) return ret; dev_dbg(df->dev.parent, "Enabled dev BW HW monitor governor\n"); break; case DEVFREQ_GOV_STOP: sysfs_remove_group(&df->dev.kobj, &dev_attr_group); devfreq_monitor_stop(df); *(unsigned long *)df->data = 0; stop_monitoring(df); pr_debug("Disabled CPU BW HW monitor governor\n"); dev_dbg(df->dev.parent, "Disabled dev BW HW monitor governor\n"); break; case DEVFREQ_GOV_INTERVAL: Loading @@ -269,31 +365,73 @@ static int devfreq_cpubw_hwmon_ev_handler(struct devfreq *df, return 0; } static struct devfreq_governor devfreq_cpubw_hwmon = { .name = "cpubw_hwmon", .get_target_freq = devfreq_cpubw_hwmon_get_freq, .event_handler = devfreq_cpubw_hwmon_ev_handler, static struct devfreq_governor devfreq_gov_bw_hwmon = { .name = "bw_hwmon", .get_target_freq = devfreq_bw_hwmon_get_freq, .event_handler = devfreq_bw_hwmon_ev_handler, }; int register_cpubw_hwmon(struct cpubw_hwmon *hwmon) int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon) { int ret; int ret = 0; struct hwmon_node *node; struct attribute_group *attr_grp; if (!hwmon->gov && !hwmon->dev && !hwmon->of_node) return -EINVAL; if (hw != NULL) { pr_err("cpubw hwmon already registered\n"); return -EBUSY; node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); if (!node) { dev_err(dev, "Unable to register gov. Out of memory!\n"); return -ENOMEM; } hw = hwmon; if (hwmon->gov) { attr_grp = devm_kzalloc(dev, sizeof(*attr_grp), GFP_KERNEL); if (!attr_grp) return -ENOMEM; ret = devfreq_add_governor(&devfreq_cpubw_hwmon); if (ret) { pr_err("devfreq governor registration failed\n"); return ret; hwmon->gov->get_target_freq = devfreq_bw_hwmon_get_freq; hwmon->gov->event_handler = devfreq_bw_hwmon_ev_handler; attr_grp->name = hwmon->gov->name; attr_grp->attrs = dev_attr; node->gov = hwmon->gov; node->attr_grp = attr_grp; } else { node->gov = &devfreq_gov_bw_hwmon; node->attr_grp = &dev_attr_group; } return 0; node->tolerance_percent = 10; node->guard_band_mbps = 100; node->decay_rate = 90; node->io_percent = 16; node->bw_step = 190; node->hw = hwmon; mutex_lock(&list_lock); list_add_tail(&node->list, &hwmon_list); mutex_unlock(&list_lock); if (hwmon->gov) { ret = devfreq_add_governor(hwmon->gov); } else { mutex_lock(&state_lock); if (!use_cnt) ret = devfreq_add_governor(&devfreq_gov_bw_hwmon); if (!ret) use_cnt++; mutex_unlock(&state_lock); } if (!ret) dev_info(dev, "BW HWmon governor registered.\n"); else dev_err(dev, "BW HWmon governor registration failed!\n"); return ret; } MODULE_DESCRIPTION("HW monitor based CPU DDR bandwidth voting driver"); MODULE_DESCRIPTION("HW monitor based dev DDR bandwidth voting driver"); MODULE_LICENSE("GPL v2");
drivers/devfreq/governor_cpubw_hwmon.h→drivers/devfreq/governor_bw_hwmon.h +71 −0 Original line number Diff line number Diff line Loading @@ -11,38 +11,61 @@ * GNU General Public License for more details. */ #ifndef _GOVERNOR_CPUBW_HWMON_H #define _GOVERNOR_CPUBW_HWMON_H #ifndef _GOVERNOR_BW_HWMON_H #define _GOVERNOR_BW_HWMON_H #include <linux/kernel.h> #include <linux/devfreq.h> /** * struct cpubw_hwmon - CPU BW HW monitor ops * @start_hwmon: Start the HW monitoring of the CPU BW * @stop_hwmon: Stop the HW monitoring of CPU BW * struct bw_hwmon - dev BW HW monitor info * @start_hwmon: Start the HW monitoring of the dev BW * @stop_hwmon: Stop the HW monitoring of dev BW * @is_valid_irq: Check whether the IRQ was triggered by the * counters used to monitor CPU BW. * counters used to monitor dev BW. * @meas_bw_and_set_irq: Return the measured bandwidth and set up the * IRQ to fire if the usage exceeds current * measurement by @tol percent. * @irq: IRQ number that corresponds to this HW * monitor. * @dev: Pointer to device that this HW monitor can * monitor. * @of_node: OF node of device that this HW monitor can * monitor. * @gov: devfreq_governor struct that should be used * when registering this HW monitor with devfreq. * Only the name field is expected to be * initialized. * @df: Devfreq node that this HW monitor is being * used for. NULL when not actively in use and * non-NULL when in use. * * One of dev, of_node or governor_name needs to be specified for a * successful registration. * */ struct cpubw_hwmon { int (*start_hwmon)(struct devfreq *df, unsigned long mbps); void (*stop_hwmon)(struct devfreq *df); bool (*is_valid_irq)(struct devfreq *df); unsigned long (*meas_bw_and_set_irq)(struct devfreq *df, struct bw_hwmon { int (*start_hwmon)(struct bw_hwmon *hw, unsigned long mbps); void (*stop_hwmon)(struct bw_hwmon *hw); bool (*is_valid_irq)(struct bw_hwmon *hw); unsigned long (*meas_bw_and_set_irq)(struct bw_hwmon *hw, unsigned int tol, unsigned int us); int irq; struct device *dev; struct device_node *of_node; struct devfreq_governor *gov; struct devfreq *df; }; #ifdef CONFIG_DEVFREQ_GOV_MSM_CPUBW_HWMON int register_cpubw_hwmon(struct cpubw_hwmon *hwmon); #ifdef CONFIG_DEVFREQ_GOV_MSM_BW_HWMON int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon); #else static inline int register_cpubw_hwmon(struct cpubw_hwmon *hwmon) static inline int register_bw_hwmon(struct device *dev, struct bw_hwmon *hwmon) { return 0; } #endif #endif /* _GOVERNOR_CPUBW_HWMON_H */ #endif /* _GOVERNOR_BW_HWMON_H */