From 77d4c0bb9c54e9c17b9d2ea66465a3f00d425694 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 16 Jan 2019 20:15:44 -0800 Subject: [PATCH] PM / devfreq: bw_hwmon: Split out sw and hw paths Let's split out the sw and hw counter configuration code paths indicated by has_hw_sampling() into inline functions for the two different types of monitors. This allows us to add different types of monitors in the future with minimal changes. Change-Id: I5cf6a1fe4d84ee0958fe68601cb1e76836d10256 Signed-off-by: Stephen Boyd [avajid@codeaurora.org: minor change to check ret < 0] Signed-off-by: Amir Vajid --- drivers/devfreq/bimc-bwmon.c | 457 ++++++++++++++++++++++++----------- 1 file changed, 322 insertions(+), 135 deletions(-) diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 6b9df0478b1f..5a4305822993 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -46,6 +46,11 @@ #define MON2_ZONE_CNT(m) ((m)->base + 0x2D8) #define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone) +enum mon_reg_type { + MON1, + MON2, +}; + struct bwmon_spec { bool wrap_on_thres; bool overflow; @@ -68,7 +73,6 @@ struct bwmon { }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) -#define has_hw_sampling(m) (m->spec->hw_sampling) #define ENABLE_MASK BIT(0) #define THROTTLE_MASK 0x1F @@ -78,20 +82,29 @@ struct bwmon { #define INT_STATUS_MASK_HWS 0xF0 static DEFINE_SPINLOCK(glb_lock); -static void mon_enable(struct bwmon *m) + +static __always_inline void mon_enable(struct bwmon *m, enum mon_reg_type type) { - if (has_hw_sampling(m)) - writel_relaxed((ENABLE_MASK | m->throttle_adj), MON2_EN(m)); - else - writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m)); + switch (type) { + case MON1: + writel_relaxed(ENABLE_MASK | m->throttle_adj, MON_EN(m)); + break; + case MON2: + writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m)); + break; + } } -static void mon_disable(struct bwmon *m) +static __always_inline void mon_disable(struct bwmon *m, enum mon_reg_type type) { - if (has_hw_sampling(m)) - writel_relaxed(m->throttle_adj, MON2_EN(m)); - else + switch (type) { + case MON1: writel_relaxed(m->throttle_adj, MON_EN(m)); + break; + case MON2: + writel_relaxed(m->throttle_adj, MON2_EN(m)); + break; + } /* * mon_disable() and mon_irq_clear(), * If latter goes first and count happen to trigger irq, we would @@ -102,24 +115,25 @@ static void mon_disable(struct bwmon *m) #define MON_CLEAR_BIT 0x1 #define MON_CLEAR_ALL_BIT 0x2 -static void mon_clear(struct bwmon *m, bool clear_all) +static __always_inline +void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) { - if (!has_hw_sampling(m)) { + switch (type) { + case MON1: writel_relaxed(MON_CLEAR_BIT, MON_CLEAR(m)); - goto out; + break; + case MON2: + if (clear_all) + writel_relaxed(MON_CLEAR_ALL_BIT, MON2_CLEAR(m)); + else + writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m)); + break; } - - if (clear_all) - writel_relaxed(MON_CLEAR_ALL_BIT, MON2_CLEAR(m)); - else - writel_relaxed(MON_CLEAR_BIT, MON2_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. */ -out: mb(); } @@ -140,74 +154,141 @@ static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) } } -static void mon_irq_enable(struct bwmon *m) +static void mon_glb_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)); - - val = readl_relaxed(MON_INT_EN(m)); - val |= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_ENABLE_V1; - writel_relaxed(val, MON_INT_EN(m)); - spin_unlock(&glb_lock); - /* - * make Sure irq enable complete for local and global - * to avoid race with other monitor calls - */ - mb(); } -static void mon_irq_disable(struct bwmon *m) +static __always_inline +void mon_irq_enable(struct bwmon *m, enum mon_reg_type type) { u32 val; spin_lock(&glb_lock); - val = readl_relaxed(GLB_INT_EN(m)); - val &= ~(1 << m->mport); - writel_relaxed(val, GLB_INT_EN(m)); - - val = readl_relaxed(MON_INT_EN(m)); - val &= has_hw_sampling(m) ? ~INT_STATUS_MASK_HWS : ~INT_ENABLE_V1; - writel_relaxed(val, MON_INT_EN(m)); + switch (type) { + case MON1: + mon_glb_irq_enable(m); + val = readl_relaxed(MON_INT_EN(m)); + val |= INT_ENABLE_V1; + writel_relaxed(val, MON_INT_EN(m)); + break; + case MON2: + mon_glb_irq_enable(m); + val = readl_relaxed(MON_INT_EN(m)); + val |= INT_STATUS_MASK_HWS; + writel_relaxed(val, MON_INT_EN(m)); + break; + } spin_unlock(&glb_lock); /* - * make Sure irq disable complete for local and global + * make sure irq enable complete for local and global * to avoid race with other monitor calls */ mb(); } -static unsigned int mon_irq_status(struct bwmon *m) +static void mon_glb_irq_disable(struct bwmon *m) +{ + u32 val; + + val = readl_relaxed(GLB_INT_EN(m)); + val &= ~(1 << m->mport); + writel_relaxed(val, GLB_INT_EN(m)); +} + +static __always_inline +void mon_irq_disable(struct bwmon *m, enum mon_reg_type type) +{ + u32 val; + + spin_lock(&glb_lock); + + switch (type) { + case MON1: + mon_glb_irq_disable(m); + val = readl_relaxed(MON_INT_EN(m)); + val &= ~INT_ENABLE_V1; + writel_relaxed(val, MON_INT_EN(m)); + break; + case MON2: + mon_glb_irq_disable(m); + val = readl_relaxed(MON_INT_EN(m)); + val &= ~INT_STATUS_MASK_HWS; + writel_relaxed(val, MON_INT_EN(m)); + break; + } + spin_unlock(&glb_lock); + /* + * make sure irq disable complete for local and global + * to avoid race with other monitor calls + */ + mb(); +} + +static __always_inline +unsigned int mon_irq_status(struct bwmon *m, enum mon_reg_type type) { 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))); - - mval &= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK; + switch (type) { + case MON1: + 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))); + mval &= INT_STATUS_MASK; + break; + case MON2: + 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))); + mval &= INT_STATUS_MASK_HWS; + break; + } return mval; } -static void mon_irq_clear(struct bwmon *m) + +static void mon_glb_irq_clear(struct bwmon *m) { - u32 intclr; - - intclr = has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK; - - writel_relaxed(intclr, MON_INT_CLR(m)); - /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ + /* + * Synchronize the local interrupt clear in mon_irq_clear() + * with the global interrupt clear here. Otherwise, the CPU + * may reorder the two writes and clear the global interrupt + * before the local interrupt, causing the global interrupt + * to be retriggered by the local interrupt still being high. + */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); - /* Ensure the GLB IRQ clear is complete */ + /* + * Similarly, because the global registers are in a different + * region than the local registers, we need to ensure any register + * writes to enable the monitor after this call are ordered with the + * clearing here so that local writes don't happen before the + * interrupt is cleared. + */ mb(); } +static __always_inline +void mon_irq_clear(struct bwmon *m, enum mon_reg_type type) +{ + switch (type) { + case MON1: + writel_relaxed(INT_STATUS_MASK, MON_INT_CLR(m)); + mon_glb_irq_clear(m); + break; + case MON2: + writel_relaxed(INT_STATUS_MASK_HWS, MON_INT_CLR(m)); + mon_glb_irq_clear(m); + break; + } +} + static int mon_set_throttle_adj(struct bw_hwmon *hw, uint adj) { struct bwmon *m = to_bwmon(hw); @@ -325,12 +406,12 @@ static u32 mon_get_limit(struct bwmon *m) #define THRES_HIT(status) (status & BIT(0)) #define OVERFLOW(status) (status & BIT(1)) -static unsigned long mon_get_count(struct bwmon *m) +static unsigned long mon_get_count1(struct bwmon *m) { unsigned long count, status; count = readl_relaxed(MON_CNT(m)); - status = mon_irq_status(m); + status = mon_irq_status(m, MON1); dev_dbg(m->dev, "Counter: %08lx\n", count); @@ -379,6 +460,23 @@ static unsigned long mon_get_zone_stats(struct bwmon *m) return count; } +static __always_inline +unsigned long mon_get_count(struct bwmon *m, enum mon_reg_type type) +{ + unsigned long count; + + switch (type) { + case MON1: + count = mon_get_count1(m); + break; + case MON2: + count = mon_get_zone_stats(m); + break; + } + + return count; +} + /* ********** CPUBW specific code ********** */ /* Returns MBps of read/writes for the sampling window. */ @@ -392,30 +490,41 @@ static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms, return mbps; } -static unsigned long get_bytes_and_clear(struct bw_hwmon *hw) +static __always_inline +unsigned long __get_bytes_and_clear(struct bw_hwmon *hw, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); unsigned long count; - mon_disable(m); - count = has_hw_sampling(m) ? mon_get_zone_stats(m) : mon_get_count(m); - mon_clear(m, false); - mon_irq_clear(m); - mon_enable(m); + mon_disable(m, type); + count = mon_get_count(m, type); + mon_clear(m, false, type); + mon_irq_clear(m, type); + mon_enable(m, type); return count; } +static unsigned long get_bytes_and_clear(struct bw_hwmon *hw) +{ + return __get_bytes_and_clear(hw, MON1); +} + +static unsigned long get_bytes_and_clear2(struct bw_hwmon *hw) +{ + return __get_bytes_and_clear(hw, MON2); +} + static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) { unsigned long count; u32 limit; struct bwmon *m = to_bwmon(hw); - mon_disable(m); - count = mon_get_count(m); - mon_clear(m, false); - mon_irq_clear(m); + mon_disable(m, MON1); + count = mon_get_count1(m); + mon_clear(m, false, MON1); + mon_irq_clear(m, MON1); if (likely(!m->spec->wrap_on_thres)) limit = bytes; @@ -423,7 +532,7 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) limit = max(bytes, 500000UL); mon_set_limit(m, limit); - mon_enable(m); + mon_enable(m, MON1); return count; } @@ -432,21 +541,22 @@ static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms) { struct bwmon *m = to_bwmon(hw); - mon_disable(m); - mon_clear(m, false); - mon_irq_clear(m); + mon_disable(m, MON2); + mon_clear(m, false, MON2); + mon_irq_clear(m, MON2); mon_set_zones(m, sample_ms); - mon_enable(m); + mon_enable(m, MON2); return 0; } -static irqreturn_t bwmon_intr_handler(int irq, void *dev) +static irqreturn_t +__bwmon_intr_handler(int irq, void *dev, enum mon_reg_type type) { struct bwmon *m = dev; - m->intr_status = mon_irq_status(m); + m->intr_status = mon_irq_status(m, type); if (!m->intr_status) return IRQ_NONE; @@ -456,6 +566,16 @@ static irqreturn_t bwmon_intr_handler(int irq, void *dev) return IRQ_HANDLED; } +static irqreturn_t bwmon_intr_handler(int irq, void *dev) +{ + return __bwmon_intr_handler(irq, dev, MON1); +} + +static irqreturn_t bwmon_intr_handler2(int irq, void *dev) +{ + return __bwmon_intr_handler(irq, dev, MON2); +} + static irqreturn_t bwmon_intr_thread(int irq, void *dev) { struct bwmon *m = dev; @@ -464,85 +584,150 @@ static irqreturn_t bwmon_intr_thread(int irq, void *dev) return IRQ_HANDLED; } -static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) +static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw, + unsigned long mbps, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); - u32 limit; - u32 zone_actions = calc_zone_actions(); + u32 limit, zone_actions; int ret; + irq_handler_t handler; - ret = request_threaded_irq(m->irq, bwmon_intr_handler, - bwmon_intr_thread, + switch (type) { + case MON1: + handler = bwmon_intr_handler; + limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0); + break; + case MON2: + zone_actions = calc_zone_actions(); + handler = bwmon_intr_handler2; + break; + } + + ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread, IRQF_ONESHOT | IRQF_SHARED, dev_name(m->dev), m); if (ret < 0) { dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", - ret); + ret); return ret; } - mon_disable(m); + mon_disable(m, type); - mon_clear(m, true); - limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0); - if (has_hw_sampling(m)) { + mon_clear(m, false, type); + + switch (type) { + case MON1: + mon_set_limit(m, limit); + break; + case MON2: mon_set_zones(m, hw->df->profile->polling_ms); /* Set the zone actions to increment appropriate counters */ writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m)); - } else { - mon_set_limit(m, limit); + break; } - mon_irq_clear(m); - mon_irq_enable(m); - mon_enable(m); + mon_irq_clear(m, type); + mon_irq_enable(m, type); + mon_enable(m, type); return 0; } -static void stop_bw_hwmon(struct bw_hwmon *hw) +static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) +{ + return __start_bw_hwmon(hw, mbps, MON1); +} + +static int start_bw_hwmon2(struct bw_hwmon *hw, unsigned long mbps) +{ + return __start_bw_hwmon(hw, mbps, MON2); +} + +static __always_inline +void __stop_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); - mon_irq_disable(m); + mon_irq_disable(m, type); free_irq(m->irq, m); - mon_disable(m); - mon_clear(m, true); - mon_irq_clear(m); + mon_disable(m, type); + mon_clear(m, true, type); + mon_irq_clear(m, type); +} + +static void stop_bw_hwmon(struct bw_hwmon *hw) +{ + return __stop_bw_hwmon(hw, MON1); +} + +static void stop_bw_hwmon2(struct bw_hwmon *hw) +{ + return __stop_bw_hwmon(hw, MON2); +} + +static __always_inline +int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) +{ + struct bwmon *m = to_bwmon(hw); + + mon_irq_disable(m, type); + free_irq(m->irq, m); + mon_disable(m, type); + mon_irq_clear(m, type); + + return 0; } static int suspend_bw_hwmon(struct bw_hwmon *hw) { - struct bwmon *m = to_bwmon(hw); + return __suspend_bw_hwmon(hw, MON1); +} - mon_irq_disable(m); - free_irq(m->irq, m); - mon_disable(m); - mon_irq_clear(m); +static int suspend_bw_hwmon2(struct bw_hwmon *hw) +{ + return __suspend_bw_hwmon(hw, MON2); +} + +static int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) +{ + struct bwmon *m = to_bwmon(hw); + int ret; + irq_handler_t handler; + + switch (type) { + case MON1: + handler = bwmon_intr_handler; + break; + case MON2: + handler = bwmon_intr_handler2; + break; + } + + mon_clear(m, false, type); + ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(m->dev), m); + if (ret < 0) { + dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", + ret); + return ret; + } + + mon_irq_enable(m, type); + mon_enable(m, type); return 0; } static int resume_bw_hwmon(struct bw_hwmon *hw) { - struct bwmon *m = to_bwmon(hw); - int ret; + return __resume_bw_hwmon(hw, MON1); +} - mon_clear(m, false); - ret = request_threaded_irq(m->irq, bwmon_intr_handler, - bwmon_intr_thread, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(m->dev), m); - if (ret < 0) { - dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", - ret); - return ret; - } - - mon_irq_enable(m); - mon_enable(m); - - return 0; +static int resume_bw_hwmon2(struct bw_hwmon *hw) +{ + return __resume_bw_hwmon(hw, MON2); } /*************************************************************************/ @@ -608,17 +793,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return -ENODEV; } - if (has_hw_sampling(m)) { - ret = of_property_read_u32(dev->of_node, "qcom,hw-timer-hz", - &data); - if (ret < 0) { - dev_err(dev, "HW sampling rate not specified! (%d)\n", - ret); - return ret; - } - m->hw_timer_hz = data; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); @@ -651,15 +825,28 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) 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.get_bytes_and_clear = get_bytes_and_clear; - m->hw.set_thres = set_thres; + if (m->spec->hw_sampling) { + ret = of_property_read_u32(dev->of_node, "qcom,hw-timer-hz", + &m->hw_timer_hz); + if (ret < 0) { + dev_err(dev, "HW sampling rate not specified!\n"); + return ret; + } - if (has_hw_sampling(m)) + m->hw.start_hwmon = start_bw_hwmon2; + m->hw.stop_hwmon = stop_bw_hwmon2; + m->hw.suspend_hwmon = suspend_bw_hwmon2; + m->hw.resume_hwmon = resume_bw_hwmon2; + m->hw.get_bytes_and_clear = get_bytes_and_clear2; m->hw.set_hw_events = set_hw_events; + } else { + 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.get_bytes_and_clear = get_bytes_and_clear; + m->hw.set_thres = set_thres; + } if (m->spec->throt_adj) { m->hw.set_throttle_adj = mon_set_throttle_adj;