Loading Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ can be used to measure the bandwidth of read/write traffic from the BIMC master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: - compatible: Must be "qcom,bimc-bwmon" - compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected Loading drivers/devfreq/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,15 @@ config DEVFREQ_GOV_PASSIVE through sysfs entries. The passive governor recommends that devfreq device uses the OPP table to get the frequency/voltage. config QCOM_BIMC_BWMON tristate "QCOM BIMC Bandwidth monitor hardware" depends on ARCH_QCOM help The BIMC Bandwidth monitor hardware allows for monitoring the traffic coming from each master port connected to the BIMC. It also has the capability to raise an IRQ when the count exceeds a programmable limit. config DEVFREQ_GOV_QCOM_BW_HWMON tristate "HW monitor based governor for device BW" depends on QCOM_BIMC_BWMON Loading drivers/devfreq/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o obj-$(CONFIG_QCOM_BIMC_BWMON) += bimc-bwmon.o obj-$(CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON) += governor_bw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON) += governor_cache_hwmon.o Loading drivers/devfreq/bimc-bwmon.c 0 → 100644 +357 −0 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. */ #define pr_fmt(fmt) "bimc-bwmon: " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/spinlock.h> #include "governor_bw_hwmon.h" #define GLB_INT_STATUS(m) ((m)->global_base + 0x100) #define GLB_INT_CLR(m) ((m)->global_base + 0x108) #define GLB_INT_EN(m) ((m)->global_base + 0x10C) #define MON_INT_STATUS(m) ((m)->base + 0x100) #define MON_INT_CLR(m) ((m)->base + 0x108) #define MON_INT_EN(m) ((m)->base + 0x10C) #define MON_EN(m) ((m)->base + 0x280) #define MON_CLEAR(m) ((m)->base + 0x284) #define MON_CNT(m) ((m)->base + 0x288) #define MON_THRES(m) ((m)->base + 0x290) #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) struct bwmon { void __iomem *base; void __iomem *global_base; unsigned int mport; unsigned int irq; struct device *dev; struct bw_hwmon hw; }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) static DEFINE_SPINLOCK(glb_lock); static void mon_enable(struct bwmon *m) { writel_relaxed(0x1, MON_EN(m)); } static void mon_disable(struct bwmon *m) { writel_relaxed(0x0, MON_EN(m)); } static void mon_clear(struct bwmon *m) { writel_relaxed(0x1, MON_CLEAR(m)); /* * The counter clear and IRQ clear bits are not in the same 4KB * region. So, we need to make sure the counter clear is completed * before we try to clear the IRQ or do any other counter operations. */ mb(); } static void mon_irq_enable(struct bwmon *m) { u32 val; spin_lock(&glb_lock); val = readl_relaxed(GLB_INT_EN(m)); val |= 1 << m->mport; writel_relaxed(val, GLB_INT_EN(m)); spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); val |= 0x1; writel_relaxed(val, MON_INT_EN(m)); } static void mon_irq_disable(struct bwmon *m) { u32 val; spin_lock(&glb_lock); val = readl_relaxed(GLB_INT_EN(m)); val &= ~(1 << m->mport); writel_relaxed(val, GLB_INT_EN(m)); spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); val &= ~0x1; writel_relaxed(val, MON_INT_EN(m)); } static int mon_irq_status(struct bwmon *m) { u32 mval; mval = readl_relaxed(MON_INT_STATUS(m)); dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); return mval & 0x1; } static void mon_irq_clear(struct bwmon *m) { writel_relaxed(0x1, MON_INT_CLR(m)); /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); /* Ensure the GLB IRQ clear is complete */ mb(); } static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); dev_dbg(m->dev, "Thres: %08x\n", count); } static u32 mon_get_limit(struct bwmon *m) { return readl_relaxed(MON_THRES(m)); } static unsigned long mon_get_count(struct bwmon *m) { unsigned long count; count = readl_relaxed(MON_CNT(m)); dev_dbg(m->dev, "Counter: %08lx\n", count); if (mon_irq_status(m)) count += mon_get_limit(m); dev_dbg(m->dev, "Actual Count: %08lx\n", count); return count; } /* ********** CPUBW specific code ********** */ /* Returns MBps of read/writes for the sampling window. */ static unsigned int bytes_to_mbps(long long bytes, unsigned int us) { bytes *= USEC_PER_SEC; do_div(bytes, us); bytes = DIV_ROUND_UP_ULL(bytes, SZ_1M); return bytes; } static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms, unsigned int tolerance_percent) { mbps *= (100 + tolerance_percent) * ms; mbps /= 100; mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC); mbps *= SZ_1M; return mbps; } static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw, unsigned int tol, unsigned int us) { unsigned long mbps; u32 limit; unsigned int sample_ms = hw->df->profile->polling_ms; struct bwmon *m = to_bwmon(hw); mon_disable(m); 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. */ limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); mon_set_limit(m, limit); mon_clear(m); mon_irq_clear(m); mon_enable(m); dev_dbg(m->dev, "MBps = %lu\n", mbps); return mbps; } static irqreturn_t bwmon_intr_handler(int irq, void *dev) { struct bwmon *m = dev; if (mon_irq_status(m)) { update_bw_hwmon(&m->hw); return IRQ_HANDLED; } return IRQ_NONE; } static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) { struct bwmon *m = to_bwmon(hw); u32 limit; int ret; ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, dev_name(m->dev), m); if (ret) { dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", ret); return ret; } mon_disable(m); limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0); mon_set_limit(m, limit); mon_clear(m); mon_irq_clear(m); mon_irq_enable(m); mon_enable(m); return 0; } static void stop_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); free_irq(m->irq, m); mon_disable(m); mon_irq_disable(m); mon_clear(m); mon_irq_clear(m); } static int suspend_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); free_irq(m->irq, m); mon_disable(m); mon_irq_disable(m); mon_irq_clear(m); return 0; } static int resume_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); int ret; mon_clear(m); mon_irq_enable(m); mon_enable(m); ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, dev_name(m->dev), m); if (ret) { dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", ret); return ret; } return 0; } /*************************************************************************/ static int bimc_bwmon_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct bwmon *m; int ret; u32 data; m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL); if (!m) return -ENOMEM; m->dev = dev; ret = of_property_read_u32(dev->of_node, "qcom,mport", &data); if (ret) { dev_err(dev, "mport not found!\n"); return ret; } m->mport = data; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); return -EINVAL; } m->base = devm_ioremap(dev, res->start, resource_size(res)); if (!m->base) { dev_err(dev, "Unable map base!\n"); return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base"); if (!res) { dev_err(dev, "global_base not found!\n"); return -EINVAL; } m->global_base = devm_ioremap(dev, res->start, resource_size(res)); if (!m->global_base) { dev_err(dev, "Unable map global_base!\n"); return -ENOMEM; } m->irq = platform_get_irq(pdev, 0); if (m->irq < 0) { dev_err(dev, "Unable to get IRQ number\n"); return m->irq; } m->hw.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0); if (!m->hw.of_node) return -EINVAL; m->hw.start_hwmon = &start_bw_hwmon; m->hw.stop_hwmon = &stop_bw_hwmon; m->hw.suspend_hwmon = &suspend_bw_hwmon; m->hw.resume_hwmon = &resume_bw_hwmon; m->hw.meas_bw_and_set_irq = &meas_bw_and_set_irq; ret = register_bw_hwmon(dev, &m->hw); if (ret) { dev_err(dev, "Dev BW hwmon registration failed\n"); return ret; } 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 = { .name = "bimc-bwmon", .of_match_table = bimc_bwmon_match_table, }, }; module_platform_driver(bimc_bwmon_driver); MODULE_DESCRIPTION("BIMC bandwidth monitor driver"); MODULE_LICENSE("GPL v2"); drivers/devfreq/devfreq_devbw.c +19 −5 Original line number Diff line number Diff line Loading @@ -206,11 +206,6 @@ int devfreq_add_devbw(struct device *dev) return 0; } static int devfreq_devbw_probe(struct platform_device *pdev) { return devfreq_add_devbw(&pdev->dev); } int devfreq_remove_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); Loading @@ -220,6 +215,25 @@ int devfreq_remove_devbw(struct device *dev) return 0; } int devfreq_suspend_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); return devfreq_suspend_device(d->df); } int devfreq_resume_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); return devfreq_resume_device(d->df); } static int devfreq_devbw_probe(struct platform_device *pdev) { return devfreq_add_devbw(&pdev->dev); } static int devfreq_devbw_remove(struct platform_device *pdev) { return devfreq_remove_devbw(&pdev->dev); Loading Loading
Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ can be used to measure the bandwidth of read/write traffic from the BIMC master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: - compatible: Must be "qcom,bimc-bwmon" - compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected Loading
drivers/devfreq/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,15 @@ config DEVFREQ_GOV_PASSIVE through sysfs entries. The passive governor recommends that devfreq device uses the OPP table to get the frequency/voltage. config QCOM_BIMC_BWMON tristate "QCOM BIMC Bandwidth monitor hardware" depends on ARCH_QCOM help The BIMC Bandwidth monitor hardware allows for monitoring the traffic coming from each master port connected to the BIMC. It also has the capability to raise an IRQ when the count exceeds a programmable limit. config DEVFREQ_GOV_QCOM_BW_HWMON tristate "HW monitor based governor for device BW" depends on QCOM_BIMC_BWMON Loading
drivers/devfreq/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o obj-$(CONFIG_QCOM_BIMC_BWMON) += bimc-bwmon.o obj-$(CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON) += governor_bw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON) += governor_cache_hwmon.o Loading
drivers/devfreq/bimc-bwmon.c 0 → 100644 +357 −0 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. */ #define pr_fmt(fmt) "bimc-bwmon: " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/spinlock.h> #include "governor_bw_hwmon.h" #define GLB_INT_STATUS(m) ((m)->global_base + 0x100) #define GLB_INT_CLR(m) ((m)->global_base + 0x108) #define GLB_INT_EN(m) ((m)->global_base + 0x10C) #define MON_INT_STATUS(m) ((m)->base + 0x100) #define MON_INT_CLR(m) ((m)->base + 0x108) #define MON_INT_EN(m) ((m)->base + 0x10C) #define MON_EN(m) ((m)->base + 0x280) #define MON_CLEAR(m) ((m)->base + 0x284) #define MON_CNT(m) ((m)->base + 0x288) #define MON_THRES(m) ((m)->base + 0x290) #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) struct bwmon { void __iomem *base; void __iomem *global_base; unsigned int mport; unsigned int irq; struct device *dev; struct bw_hwmon hw; }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) static DEFINE_SPINLOCK(glb_lock); static void mon_enable(struct bwmon *m) { writel_relaxed(0x1, MON_EN(m)); } static void mon_disable(struct bwmon *m) { writel_relaxed(0x0, MON_EN(m)); } static void mon_clear(struct bwmon *m) { writel_relaxed(0x1, MON_CLEAR(m)); /* * The counter clear and IRQ clear bits are not in the same 4KB * region. So, we need to make sure the counter clear is completed * before we try to clear the IRQ or do any other counter operations. */ mb(); } static void mon_irq_enable(struct bwmon *m) { u32 val; spin_lock(&glb_lock); val = readl_relaxed(GLB_INT_EN(m)); val |= 1 << m->mport; writel_relaxed(val, GLB_INT_EN(m)); spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); val |= 0x1; writel_relaxed(val, MON_INT_EN(m)); } static void mon_irq_disable(struct bwmon *m) { u32 val; spin_lock(&glb_lock); val = readl_relaxed(GLB_INT_EN(m)); val &= ~(1 << m->mport); writel_relaxed(val, GLB_INT_EN(m)); spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); val &= ~0x1; writel_relaxed(val, MON_INT_EN(m)); } static int mon_irq_status(struct bwmon *m) { u32 mval; mval = readl_relaxed(MON_INT_STATUS(m)); dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); return mval & 0x1; } static void mon_irq_clear(struct bwmon *m) { writel_relaxed(0x1, MON_INT_CLR(m)); /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); /* Ensure the GLB IRQ clear is complete */ mb(); } static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); dev_dbg(m->dev, "Thres: %08x\n", count); } static u32 mon_get_limit(struct bwmon *m) { return readl_relaxed(MON_THRES(m)); } static unsigned long mon_get_count(struct bwmon *m) { unsigned long count; count = readl_relaxed(MON_CNT(m)); dev_dbg(m->dev, "Counter: %08lx\n", count); if (mon_irq_status(m)) count += mon_get_limit(m); dev_dbg(m->dev, "Actual Count: %08lx\n", count); return count; } /* ********** CPUBW specific code ********** */ /* Returns MBps of read/writes for the sampling window. */ static unsigned int bytes_to_mbps(long long bytes, unsigned int us) { bytes *= USEC_PER_SEC; do_div(bytes, us); bytes = DIV_ROUND_UP_ULL(bytes, SZ_1M); return bytes; } static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms, unsigned int tolerance_percent) { mbps *= (100 + tolerance_percent) * ms; mbps /= 100; mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC); mbps *= SZ_1M; return mbps; } static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw, unsigned int tol, unsigned int us) { unsigned long mbps; u32 limit; unsigned int sample_ms = hw->df->profile->polling_ms; struct bwmon *m = to_bwmon(hw); mon_disable(m); 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. */ limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); mon_set_limit(m, limit); mon_clear(m); mon_irq_clear(m); mon_enable(m); dev_dbg(m->dev, "MBps = %lu\n", mbps); return mbps; } static irqreturn_t bwmon_intr_handler(int irq, void *dev) { struct bwmon *m = dev; if (mon_irq_status(m)) { update_bw_hwmon(&m->hw); return IRQ_HANDLED; } return IRQ_NONE; } static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) { struct bwmon *m = to_bwmon(hw); u32 limit; int ret; ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, dev_name(m->dev), m); if (ret) { dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", ret); return ret; } mon_disable(m); limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0); mon_set_limit(m, limit); mon_clear(m); mon_irq_clear(m); mon_irq_enable(m); mon_enable(m); return 0; } static void stop_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); free_irq(m->irq, m); mon_disable(m); mon_irq_disable(m); mon_clear(m); mon_irq_clear(m); } static int suspend_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); free_irq(m->irq, m); mon_disable(m); mon_irq_disable(m); mon_irq_clear(m); return 0; } static int resume_bw_hwmon(struct bw_hwmon *hw) { struct bwmon *m = to_bwmon(hw); int ret; mon_clear(m); mon_irq_enable(m); mon_enable(m); ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler, IRQF_ONESHOT | IRQF_SHARED, dev_name(m->dev), m); if (ret) { dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", ret); return ret; } return 0; } /*************************************************************************/ static int bimc_bwmon_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct bwmon *m; int ret; u32 data; m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL); if (!m) return -ENOMEM; m->dev = dev; ret = of_property_read_u32(dev->of_node, "qcom,mport", &data); if (ret) { dev_err(dev, "mport not found!\n"); return ret; } m->mport = data; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); return -EINVAL; } m->base = devm_ioremap(dev, res->start, resource_size(res)); if (!m->base) { dev_err(dev, "Unable map base!\n"); return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base"); if (!res) { dev_err(dev, "global_base not found!\n"); return -EINVAL; } m->global_base = devm_ioremap(dev, res->start, resource_size(res)); if (!m->global_base) { dev_err(dev, "Unable map global_base!\n"); return -ENOMEM; } m->irq = platform_get_irq(pdev, 0); if (m->irq < 0) { dev_err(dev, "Unable to get IRQ number\n"); return m->irq; } m->hw.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0); if (!m->hw.of_node) return -EINVAL; m->hw.start_hwmon = &start_bw_hwmon; m->hw.stop_hwmon = &stop_bw_hwmon; m->hw.suspend_hwmon = &suspend_bw_hwmon; m->hw.resume_hwmon = &resume_bw_hwmon; m->hw.meas_bw_and_set_irq = &meas_bw_and_set_irq; ret = register_bw_hwmon(dev, &m->hw); if (ret) { dev_err(dev, "Dev BW hwmon registration failed\n"); return ret; } 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 = { .name = "bimc-bwmon", .of_match_table = bimc_bwmon_match_table, }, }; module_platform_driver(bimc_bwmon_driver); MODULE_DESCRIPTION("BIMC bandwidth monitor driver"); MODULE_LICENSE("GPL v2");
drivers/devfreq/devfreq_devbw.c +19 −5 Original line number Diff line number Diff line Loading @@ -206,11 +206,6 @@ int devfreq_add_devbw(struct device *dev) return 0; } static int devfreq_devbw_probe(struct platform_device *pdev) { return devfreq_add_devbw(&pdev->dev); } int devfreq_remove_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); Loading @@ -220,6 +215,25 @@ int devfreq_remove_devbw(struct device *dev) return 0; } int devfreq_suspend_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); return devfreq_suspend_device(d->df); } int devfreq_resume_devbw(struct device *dev) { struct dev_data *d = dev_get_drvdata(dev); return devfreq_resume_device(d->df); } static int devfreq_devbw_probe(struct platform_device *pdev) { return devfreq_add_devbw(&pdev->dev); } static int devfreq_devbw_remove(struct platform_device *pdev) { return devfreq_remove_devbw(&pdev->dev); Loading