Loading Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +4 −1 Original line number Diff line number Diff line Loading @@ -6,11 +6,14 @@ master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: - compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2", "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" or "qcom,bimc-bwmon5" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected bases are: "base", "global_base" "global_base" should not be specified for "qcom,bimc-bwmon5" compatibles. - interrupts: Lists the threshold IRQ. - qcom,mport: The hardware master port that this device can monitor - qcom,target-dev: The DT device that corresponds to this master port Loading drivers/devfreq/bimc-bwmon.c +267 −65 Original line number Diff line number Diff line Loading @@ -32,8 +32,12 @@ #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_STATUS_MASK 0x03 #define MON2_INT_STATUS_MASK 0xF0 #define MON2_INT_STATUS_SHIFT 4 #define MON_INT_CLR(m) ((m)->base + 0x108) #define MON_INT_EN(m) ((m)->base + 0x10C) #define MON_INT_ENABLE 0x1 #define MON_EN(m) ((m)->base + 0x280) #define MON_CLEAR(m) ((m)->base + 0x284) #define MON_CNT(m) ((m)->base + 0x288) Loading @@ -54,9 +58,27 @@ #define MON2_ZONE_CNT(m) ((m)->base + 0x2D8) #define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone) #define MON3_INT_STATUS(m) ((m)->base + 0x00) #define MON3_INT_CLR(m) ((m)->base + 0x08) #define MON3_INT_EN(m) ((m)->base + 0x0C) #define MON3_INT_STATUS_MASK 0x0F #define MON3_EN(m) ((m)->base + 0x10) #define MON3_CLEAR(m) ((m)->base + 0x14) #define MON3_SW(m) ((m)->base + 0x20) #define MON3_THRES_HI(m) ((m)->base + 0x24) #define MON3_THRES_MED(m) ((m)->base + 0x28) #define MON3_THRES_LO(m) ((m)->base + 0x2C) #define MON3_ZONE_ACTIONS(m) ((m)->base + 0x30) #define MON3_ZONE_CNT_THRES(m) ((m)->base + 0x34) #define MON3_BYTE_CNT(m) ((m)->base + 0x38) #define MON3_WIN_TIMER(m) ((m)->base + 0x3C) #define MON3_ZONE_CNT(m) ((m)->base + 0x40) #define MON3_ZONE_MAX(m, zone) ((m)->base + 0x44 + 0x4 * zone) enum mon_reg_type { MON1, MON2, MON3, }; struct bwmon_spec { Loading @@ -64,6 +86,8 @@ struct bwmon_spec { bool overflow; bool throt_adj; bool hw_sampling; bool has_global_base; enum mon_reg_type reg_type; }; struct bwmon { Loading @@ -85,9 +109,6 @@ struct bwmon { #define ENABLE_MASK BIT(0) #define THROTTLE_MASK 0x1F #define THROTTLE_SHIFT 16 #define INT_ENABLE_V1 0x1 #define INT_STATUS_MASK 0x03 #define INT_STATUS_MASK_HWS 0xF0 static DEFINE_SPINLOCK(glb_lock); Loading @@ -100,6 +121,9 @@ static __always_inline void mon_enable(struct bwmon *m, enum mon_reg_type type) case MON2: writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m)); break; case MON3: writel_relaxed(ENABLE_MASK | m->throttle_adj, MON3_EN(m)); break; } } Loading @@ -112,6 +136,9 @@ static __always_inline void mon_disable(struct bwmon *m, enum mon_reg_type type) case MON2: writel_relaxed(m->throttle_adj, MON2_EN(m)); break; case MON3: writel_relaxed(m->throttle_adj, MON3_EN(m)); break; } /* * mon_disable() and mon_irq_clear(), Loading @@ -136,6 +163,12 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) else writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m)); break; case MON3: if (clear_all) writel_relaxed(MON_CLEAR_ALL_BIT, MON3_CLEAR(m)); else writel_relaxed(MON_CLEAR_BIT, MON3_CLEAR(m)); break; } /* * The counter clear and IRQ clear bits are not in the same 4KB Loading @@ -146,7 +179,9 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) } #define SAMPLE_WIN_LIM 0xFFFFF static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) static __always_inline void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { u32 rate; Loading @@ -158,7 +193,17 @@ static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) pr_warn("Sample window %u larger than hw limit: %u\n", rate, SAMPLE_WIN_LIM); } switch (type) { case MON1: WARN(1, "Invalid\n"); return; case MON2: writel_relaxed(rate, MON2_SW(m)); break; case MON3: writel_relaxed(rate, MON3_SW(m)); break; } } } Loading @@ -181,15 +226,20 @@ void mon_irq_enable(struct bwmon *m, enum mon_reg_type type) case MON1: mon_glb_irq_enable(m); val = readl_relaxed(MON_INT_EN(m)); val |= INT_ENABLE_V1; val |= MON_INT_ENABLE; 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; val |= MON2_INT_STATUS_MASK; writel_relaxed(val, MON_INT_EN(m)); break; case MON3: val = readl_relaxed(MON3_INT_EN(m)); val |= MON3_INT_STATUS_MASK; writel_relaxed(val, MON3_INT_EN(m)); break; } spin_unlock(&glb_lock); /* Loading Loading @@ -219,15 +269,20 @@ void mon_irq_disable(struct bwmon *m, enum mon_reg_type type) case MON1: mon_glb_irq_disable(m); val = readl_relaxed(MON_INT_EN(m)); val &= ~INT_ENABLE_V1; val &= ~MON_INT_ENABLE; 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; val &= ~MON2_INT_STATUS_MASK; writel_relaxed(val, MON_INT_EN(m)); break; case MON3: val = readl_relaxed(MON3_INT_EN(m)); val &= ~MON3_INT_STATUS_MASK; writel_relaxed(val, MON3_INT_EN(m)); break; } spin_unlock(&glb_lock); /* Loading @@ -247,13 +302,19 @@ unsigned int mon_irq_status(struct bwmon *m, enum mon_reg_type type) 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; mval &= MON_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; mval &= MON2_INT_STATUS_MASK; mval >>= MON2_INT_STATUS_SHIFT; break; case MON3: mval = readl_relaxed(MON3_INT_STATUS(m)); dev_dbg(m->dev, "IRQ status p:%x\n", mval); mval &= MON3_INT_STATUS_MASK; break; } Loading Loading @@ -287,13 +348,16 @@ 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)); writel_relaxed(MON_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)); writel_relaxed(MON2_INT_STATUS_MASK, MON_INT_CLR(m)); mon_glb_irq_clear(m); break; case MON3: writel_relaxed(MON3_INT_STATUS_MASK, MON3_INT_CLR(m)); break; } } Loading Loading @@ -365,10 +429,13 @@ static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms) * Zone 3: byte count > THRES_HI */ #define THRES_LIM 0x7FFU static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) static __always_inline void set_zone_thres(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { struct bw_hwmon *hw = &(m->hw); struct bw_hwmon *hw = &m->hw; u32 hi, med, lo; u32 zone_cnt_thres = calc_zone_counts(hw); hi = mbps_to_mb(hw->up_wake_mbps, sample_ms); med = mbps_to_mb(hw->down_wake_mbps, sample_ms); Loading @@ -382,25 +449,38 @@ static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) lo = min(lo, med-1); } switch (type) { case MON1: WARN(1, "Invalid\n"); return; case MON2: writel_relaxed(hi, MON2_THRES_HI(m)); writel_relaxed(med, MON2_THRES_MED(m)); writel_relaxed(lo, MON2_THRES_LO(m)); dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo); } static void mon_set_zones(struct bwmon *m, unsigned int sample_ms) { struct bw_hwmon *hw = &(m->hw); u32 zone_cnt_thres = calc_zone_counts(hw); mon_set_hw_sampling_window(m, sample_ms); set_zone_thres(m, sample_ms); /* Set the zone count thresholds for interrupts */ writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m)); break; case MON3: writel_relaxed(hi, MON3_THRES_HI(m)); writel_relaxed(med, MON3_THRES_MED(m)); writel_relaxed(lo, MON3_THRES_LO(m)); /* Set the zone count thresholds for interrupts */ writel_relaxed(zone_cnt_thres, MON3_ZONE_CNT_THRES(m)); break; } dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo); dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres); } static __always_inline void mon_set_zones(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { mon_set_hw_sampling_window(m, sample_ms, type); set_zone_thres(m, sample_ms, type); } static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); Loading Loading @@ -433,16 +513,28 @@ static unsigned long mon_get_count1(struct bwmon *m) return count; } static unsigned int get_zone(struct bwmon *m) static __always_inline unsigned int get_zone(struct bwmon *m, enum mon_reg_type type) { u32 zone_counts; u32 zone; zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4); zone = get_bitmask_order(m->intr_status); if (zone) { zone--; } else { switch (type) { case MON1: WARN(1, "Invalid\n"); return 0; case MON2: zone_counts = readl_relaxed(MON2_ZONE_CNT(m)); break; case MON3: zone_counts = readl_relaxed(MON3_ZONE_CNT(m)); break; } if (zone_counts) { zone = get_bitmask_order(zone_counts) - 1; zone /= 8; Loading @@ -453,14 +545,35 @@ static unsigned int get_zone(struct bwmon *m) return zone; } static unsigned long mon_get_zone_stats(struct bwmon *m) static __always_inline unsigned long get_zone_count(struct bwmon *m, unsigned int zone, enum mon_reg_type type) { unsigned long count; switch (type) { case MON1: WARN(1, "Invalid\n"); return 0; case MON2: count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1; break; case MON3: count = readl_relaxed(MON3_ZONE_MAX(m, zone)) + 1; break; } return count; } static __always_inline unsigned long mon_get_zone_stats(struct bwmon *m, enum mon_reg_type type) { unsigned int zone; unsigned long count = 0; zone = get_zone(m); count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1; zone = get_zone(m, type); count = get_zone_count(m, zone, type); count *= SZ_1M; dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count); Loading @@ -478,7 +591,8 @@ unsigned long mon_get_count(struct bwmon *m, enum mon_reg_type type) count = mon_get_count1(m); break; case MON2: count = mon_get_zone_stats(m); case MON3: count = mon_get_zone_stats(m, type); break; } Loading Loading @@ -523,6 +637,11 @@ static unsigned long get_bytes_and_clear2(struct bw_hwmon *hw) return __get_bytes_and_clear(hw, MON2); } static unsigned long get_bytes_and_clear3(struct bw_hwmon *hw) { return __get_bytes_and_clear(hw, MON3); } static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) { unsigned long count; Loading @@ -545,20 +664,33 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) return count; } static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms) static unsigned long __set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); mon_disable(m, MON2); mon_clear(m, false, MON2); mon_irq_clear(m, MON2); mon_disable(m, type); mon_clear(m, false, type); mon_irq_clear(m, type); mon_set_zones(m, sample_ms); mon_enable(m, MON2); mon_set_zones(m, sample_ms, type); mon_enable(m, type); return 0; } static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms) { return __set_hw_events(hw, sample_ms, MON2); } static unsigned long set_hw_events3(struct bw_hwmon *hw, unsigned int sample_ms) { return __set_hw_events(hw, sample_ms, MON3); } static irqreturn_t __bwmon_intr_handler(int irq, void *dev, enum mon_reg_type type) { Loading @@ -584,6 +716,11 @@ static irqreturn_t bwmon_intr_handler2(int irq, void *dev) return __bwmon_intr_handler(irq, dev, MON2); } static irqreturn_t bwmon_intr_handler3(int irq, void *dev) { return __bwmon_intr_handler(irq, dev, MON3); } static irqreturn_t bwmon_intr_thread(int irq, void *dev) { struct bwmon *m = dev; Loading @@ -609,6 +746,10 @@ static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw, zone_actions = calc_zone_actions(); handler = bwmon_intr_handler2; break; case MON3: zone_actions = calc_zone_actions(); handler = bwmon_intr_handler3; break; } ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread, Loading @@ -630,10 +771,14 @@ static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw, mon_set_limit(m, limit); break; case MON2: mon_set_zones(m, hw->df->profile->polling_ms); mon_set_zones(m, hw->df->profile->polling_ms, type); /* Set the zone actions to increment appropriate counters */ writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m)); break; case MON3: mon_set_zones(m, hw->df->profile->polling_ms, type); /* Set the zone actions to increment appropriate counters */ writel_relaxed(zone_actions, MON3_ZONE_ACTIONS(m)); } mon_irq_clear(m, type); Loading @@ -653,6 +798,11 @@ static int start_bw_hwmon2(struct bw_hwmon *hw, unsigned long mbps) return __start_bw_hwmon(hw, mbps, MON2); } static int start_bw_hwmon3(struct bw_hwmon *hw, unsigned long mbps) { return __start_bw_hwmon(hw, mbps, MON3); } static __always_inline void __stop_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { Loading @@ -675,6 +825,11 @@ static void stop_bw_hwmon2(struct bw_hwmon *hw) return __stop_bw_hwmon(hw, MON2); } static void stop_bw_hwmon3(struct bw_hwmon *hw) { return __stop_bw_hwmon(hw, MON3); } static __always_inline int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { Loading @@ -698,7 +853,13 @@ 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) static int suspend_bw_hwmon3(struct bw_hwmon *hw) { return __suspend_bw_hwmon(hw, MON3); } static __always_inline int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); int ret; Loading @@ -711,6 +872,9 @@ static int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) case MON2: handler = bwmon_intr_handler2; break; case MON3: handler = bwmon_intr_handler3; break; } mon_clear(m, false, type); Loading Loading @@ -739,6 +903,11 @@ static int resume_bw_hwmon2(struct bw_hwmon *hw) return __resume_bw_hwmon(hw, MON2); } static int resume_bw_hwmon3(struct bw_hwmon *hw) { return __resume_bw_hwmon(hw, MON3); } /*************************************************************************/ static const struct bwmon_spec spec[] = { Loading @@ -746,25 +915,40 @@ static const struct bwmon_spec spec[] = { .wrap_on_thres = true, .overflow = false, .throt_adj = false, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [1] = { .wrap_on_thres = false, .overflow = true, .throt_adj = false, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [2] = { .wrap_on_thres = false, .overflow = true, .throt_adj = true, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [3] = { .wrap_on_thres = false, .overflow = true, .throt_adj = true, .hw_sampling = true .hw_sampling = true, .has_global_base = true, .reg_type = MON2, }, [4] = { .wrap_on_thres = false, .overflow = true, .throt_adj = false, .hw_sampling = true, .reg_type = MON3, }, }; Loading @@ -773,6 +957,7 @@ static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, { .compatible = "qcom,bimc-bwmon3", .data = &spec[2] }, { .compatible = "qcom,bimc-bwmon4", .data = &spec[3] }, { .compatible = "qcom,bimc-bwmon5", .data = &spec[4] }, {} }; Loading @@ -789,13 +974,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) 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; m->spec = of_device_get_match_data(dev); if (!m->spec) { dev_err(dev, "Unknown device type!\n"); Loading @@ -813,17 +991,28 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base"); if (m->spec->has_global_base) { 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)); 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; } 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; } m->irq = platform_get_irq(pdev, 0); if (m->irq < 0) { dev_err(dev, "Unable to get IRQ number\n"); Loading @@ -841,20 +1030,33 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) dev_err(dev, "HW sampling rate not specified!\n"); return ret; } } switch (m->spec->reg_type) { case MON3: m->hw.start_hwmon = start_bw_hwmon3; m->hw.stop_hwmon = stop_bw_hwmon3; m->hw.suspend_hwmon = suspend_bw_hwmon3; m->hw.resume_hwmon = resume_bw_hwmon3; m->hw.get_bytes_and_clear = get_bytes_and_clear3; m->hw.set_hw_events = set_hw_events3; break; case MON2: 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 { break; case MON1: 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; break; } if (m->spec->throt_adj) { Loading Loading
Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +4 −1 Original line number Diff line number Diff line Loading @@ -6,11 +6,14 @@ master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: - compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2", "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" or "qcom,bimc-bwmon5" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected bases are: "base", "global_base" "global_base" should not be specified for "qcom,bimc-bwmon5" compatibles. - interrupts: Lists the threshold IRQ. - qcom,mport: The hardware master port that this device can monitor - qcom,target-dev: The DT device that corresponds to this master port Loading
drivers/devfreq/bimc-bwmon.c +267 −65 Original line number Diff line number Diff line Loading @@ -32,8 +32,12 @@ #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_STATUS_MASK 0x03 #define MON2_INT_STATUS_MASK 0xF0 #define MON2_INT_STATUS_SHIFT 4 #define MON_INT_CLR(m) ((m)->base + 0x108) #define MON_INT_EN(m) ((m)->base + 0x10C) #define MON_INT_ENABLE 0x1 #define MON_EN(m) ((m)->base + 0x280) #define MON_CLEAR(m) ((m)->base + 0x284) #define MON_CNT(m) ((m)->base + 0x288) Loading @@ -54,9 +58,27 @@ #define MON2_ZONE_CNT(m) ((m)->base + 0x2D8) #define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone) #define MON3_INT_STATUS(m) ((m)->base + 0x00) #define MON3_INT_CLR(m) ((m)->base + 0x08) #define MON3_INT_EN(m) ((m)->base + 0x0C) #define MON3_INT_STATUS_MASK 0x0F #define MON3_EN(m) ((m)->base + 0x10) #define MON3_CLEAR(m) ((m)->base + 0x14) #define MON3_SW(m) ((m)->base + 0x20) #define MON3_THRES_HI(m) ((m)->base + 0x24) #define MON3_THRES_MED(m) ((m)->base + 0x28) #define MON3_THRES_LO(m) ((m)->base + 0x2C) #define MON3_ZONE_ACTIONS(m) ((m)->base + 0x30) #define MON3_ZONE_CNT_THRES(m) ((m)->base + 0x34) #define MON3_BYTE_CNT(m) ((m)->base + 0x38) #define MON3_WIN_TIMER(m) ((m)->base + 0x3C) #define MON3_ZONE_CNT(m) ((m)->base + 0x40) #define MON3_ZONE_MAX(m, zone) ((m)->base + 0x44 + 0x4 * zone) enum mon_reg_type { MON1, MON2, MON3, }; struct bwmon_spec { Loading @@ -64,6 +86,8 @@ struct bwmon_spec { bool overflow; bool throt_adj; bool hw_sampling; bool has_global_base; enum mon_reg_type reg_type; }; struct bwmon { Loading @@ -85,9 +109,6 @@ struct bwmon { #define ENABLE_MASK BIT(0) #define THROTTLE_MASK 0x1F #define THROTTLE_SHIFT 16 #define INT_ENABLE_V1 0x1 #define INT_STATUS_MASK 0x03 #define INT_STATUS_MASK_HWS 0xF0 static DEFINE_SPINLOCK(glb_lock); Loading @@ -100,6 +121,9 @@ static __always_inline void mon_enable(struct bwmon *m, enum mon_reg_type type) case MON2: writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m)); break; case MON3: writel_relaxed(ENABLE_MASK | m->throttle_adj, MON3_EN(m)); break; } } Loading @@ -112,6 +136,9 @@ static __always_inline void mon_disable(struct bwmon *m, enum mon_reg_type type) case MON2: writel_relaxed(m->throttle_adj, MON2_EN(m)); break; case MON3: writel_relaxed(m->throttle_adj, MON3_EN(m)); break; } /* * mon_disable() and mon_irq_clear(), Loading @@ -136,6 +163,12 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) else writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m)); break; case MON3: if (clear_all) writel_relaxed(MON_CLEAR_ALL_BIT, MON3_CLEAR(m)); else writel_relaxed(MON_CLEAR_BIT, MON3_CLEAR(m)); break; } /* * The counter clear and IRQ clear bits are not in the same 4KB Loading @@ -146,7 +179,9 @@ void mon_clear(struct bwmon *m, bool clear_all, enum mon_reg_type type) } #define SAMPLE_WIN_LIM 0xFFFFF static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) static __always_inline void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { u32 rate; Loading @@ -158,7 +193,17 @@ static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) pr_warn("Sample window %u larger than hw limit: %u\n", rate, SAMPLE_WIN_LIM); } switch (type) { case MON1: WARN(1, "Invalid\n"); return; case MON2: writel_relaxed(rate, MON2_SW(m)); break; case MON3: writel_relaxed(rate, MON3_SW(m)); break; } } } Loading @@ -181,15 +226,20 @@ void mon_irq_enable(struct bwmon *m, enum mon_reg_type type) case MON1: mon_glb_irq_enable(m); val = readl_relaxed(MON_INT_EN(m)); val |= INT_ENABLE_V1; val |= MON_INT_ENABLE; 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; val |= MON2_INT_STATUS_MASK; writel_relaxed(val, MON_INT_EN(m)); break; case MON3: val = readl_relaxed(MON3_INT_EN(m)); val |= MON3_INT_STATUS_MASK; writel_relaxed(val, MON3_INT_EN(m)); break; } spin_unlock(&glb_lock); /* Loading Loading @@ -219,15 +269,20 @@ void mon_irq_disable(struct bwmon *m, enum mon_reg_type type) case MON1: mon_glb_irq_disable(m); val = readl_relaxed(MON_INT_EN(m)); val &= ~INT_ENABLE_V1; val &= ~MON_INT_ENABLE; 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; val &= ~MON2_INT_STATUS_MASK; writel_relaxed(val, MON_INT_EN(m)); break; case MON3: val = readl_relaxed(MON3_INT_EN(m)); val &= ~MON3_INT_STATUS_MASK; writel_relaxed(val, MON3_INT_EN(m)); break; } spin_unlock(&glb_lock); /* Loading @@ -247,13 +302,19 @@ unsigned int mon_irq_status(struct bwmon *m, enum mon_reg_type type) 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; mval &= MON_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; mval &= MON2_INT_STATUS_MASK; mval >>= MON2_INT_STATUS_SHIFT; break; case MON3: mval = readl_relaxed(MON3_INT_STATUS(m)); dev_dbg(m->dev, "IRQ status p:%x\n", mval); mval &= MON3_INT_STATUS_MASK; break; } Loading Loading @@ -287,13 +348,16 @@ 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)); writel_relaxed(MON_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)); writel_relaxed(MON2_INT_STATUS_MASK, MON_INT_CLR(m)); mon_glb_irq_clear(m); break; case MON3: writel_relaxed(MON3_INT_STATUS_MASK, MON3_INT_CLR(m)); break; } } Loading Loading @@ -365,10 +429,13 @@ static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms) * Zone 3: byte count > THRES_HI */ #define THRES_LIM 0x7FFU static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) static __always_inline void set_zone_thres(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { struct bw_hwmon *hw = &(m->hw); struct bw_hwmon *hw = &m->hw; u32 hi, med, lo; u32 zone_cnt_thres = calc_zone_counts(hw); hi = mbps_to_mb(hw->up_wake_mbps, sample_ms); med = mbps_to_mb(hw->down_wake_mbps, sample_ms); Loading @@ -382,25 +449,38 @@ static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) lo = min(lo, med-1); } switch (type) { case MON1: WARN(1, "Invalid\n"); return; case MON2: writel_relaxed(hi, MON2_THRES_HI(m)); writel_relaxed(med, MON2_THRES_MED(m)); writel_relaxed(lo, MON2_THRES_LO(m)); dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo); } static void mon_set_zones(struct bwmon *m, unsigned int sample_ms) { struct bw_hwmon *hw = &(m->hw); u32 zone_cnt_thres = calc_zone_counts(hw); mon_set_hw_sampling_window(m, sample_ms); set_zone_thres(m, sample_ms); /* Set the zone count thresholds for interrupts */ writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m)); break; case MON3: writel_relaxed(hi, MON3_THRES_HI(m)); writel_relaxed(med, MON3_THRES_MED(m)); writel_relaxed(lo, MON3_THRES_LO(m)); /* Set the zone count thresholds for interrupts */ writel_relaxed(zone_cnt_thres, MON3_ZONE_CNT_THRES(m)); break; } dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo); dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres); } static __always_inline void mon_set_zones(struct bwmon *m, unsigned int sample_ms, enum mon_reg_type type) { mon_set_hw_sampling_window(m, sample_ms, type); set_zone_thres(m, sample_ms, type); } static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); Loading Loading @@ -433,16 +513,28 @@ static unsigned long mon_get_count1(struct bwmon *m) return count; } static unsigned int get_zone(struct bwmon *m) static __always_inline unsigned int get_zone(struct bwmon *m, enum mon_reg_type type) { u32 zone_counts; u32 zone; zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4); zone = get_bitmask_order(m->intr_status); if (zone) { zone--; } else { switch (type) { case MON1: WARN(1, "Invalid\n"); return 0; case MON2: zone_counts = readl_relaxed(MON2_ZONE_CNT(m)); break; case MON3: zone_counts = readl_relaxed(MON3_ZONE_CNT(m)); break; } if (zone_counts) { zone = get_bitmask_order(zone_counts) - 1; zone /= 8; Loading @@ -453,14 +545,35 @@ static unsigned int get_zone(struct bwmon *m) return zone; } static unsigned long mon_get_zone_stats(struct bwmon *m) static __always_inline unsigned long get_zone_count(struct bwmon *m, unsigned int zone, enum mon_reg_type type) { unsigned long count; switch (type) { case MON1: WARN(1, "Invalid\n"); return 0; case MON2: count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1; break; case MON3: count = readl_relaxed(MON3_ZONE_MAX(m, zone)) + 1; break; } return count; } static __always_inline unsigned long mon_get_zone_stats(struct bwmon *m, enum mon_reg_type type) { unsigned int zone; unsigned long count = 0; zone = get_zone(m); count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1; zone = get_zone(m, type); count = get_zone_count(m, zone, type); count *= SZ_1M; dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count); Loading @@ -478,7 +591,8 @@ unsigned long mon_get_count(struct bwmon *m, enum mon_reg_type type) count = mon_get_count1(m); break; case MON2: count = mon_get_zone_stats(m); case MON3: count = mon_get_zone_stats(m, type); break; } Loading Loading @@ -523,6 +637,11 @@ static unsigned long get_bytes_and_clear2(struct bw_hwmon *hw) return __get_bytes_and_clear(hw, MON2); } static unsigned long get_bytes_and_clear3(struct bw_hwmon *hw) { return __get_bytes_and_clear(hw, MON3); } static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) { unsigned long count; Loading @@ -545,20 +664,33 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) return count; } static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms) static unsigned long __set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); mon_disable(m, MON2); mon_clear(m, false, MON2); mon_irq_clear(m, MON2); mon_disable(m, type); mon_clear(m, false, type); mon_irq_clear(m, type); mon_set_zones(m, sample_ms); mon_enable(m, MON2); mon_set_zones(m, sample_ms, type); mon_enable(m, type); return 0; } static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned int sample_ms) { return __set_hw_events(hw, sample_ms, MON2); } static unsigned long set_hw_events3(struct bw_hwmon *hw, unsigned int sample_ms) { return __set_hw_events(hw, sample_ms, MON3); } static irqreturn_t __bwmon_intr_handler(int irq, void *dev, enum mon_reg_type type) { Loading @@ -584,6 +716,11 @@ static irqreturn_t bwmon_intr_handler2(int irq, void *dev) return __bwmon_intr_handler(irq, dev, MON2); } static irqreturn_t bwmon_intr_handler3(int irq, void *dev) { return __bwmon_intr_handler(irq, dev, MON3); } static irqreturn_t bwmon_intr_thread(int irq, void *dev) { struct bwmon *m = dev; Loading @@ -609,6 +746,10 @@ static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw, zone_actions = calc_zone_actions(); handler = bwmon_intr_handler2; break; case MON3: zone_actions = calc_zone_actions(); handler = bwmon_intr_handler3; break; } ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread, Loading @@ -630,10 +771,14 @@ static __always_inline int __start_bw_hwmon(struct bw_hwmon *hw, mon_set_limit(m, limit); break; case MON2: mon_set_zones(m, hw->df->profile->polling_ms); mon_set_zones(m, hw->df->profile->polling_ms, type); /* Set the zone actions to increment appropriate counters */ writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m)); break; case MON3: mon_set_zones(m, hw->df->profile->polling_ms, type); /* Set the zone actions to increment appropriate counters */ writel_relaxed(zone_actions, MON3_ZONE_ACTIONS(m)); } mon_irq_clear(m, type); Loading @@ -653,6 +798,11 @@ static int start_bw_hwmon2(struct bw_hwmon *hw, unsigned long mbps) return __start_bw_hwmon(hw, mbps, MON2); } static int start_bw_hwmon3(struct bw_hwmon *hw, unsigned long mbps) { return __start_bw_hwmon(hw, mbps, MON3); } static __always_inline void __stop_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { Loading @@ -675,6 +825,11 @@ static void stop_bw_hwmon2(struct bw_hwmon *hw) return __stop_bw_hwmon(hw, MON2); } static void stop_bw_hwmon3(struct bw_hwmon *hw) { return __stop_bw_hwmon(hw, MON3); } static __always_inline int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { Loading @@ -698,7 +853,13 @@ 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) static int suspend_bw_hwmon3(struct bw_hwmon *hw) { return __suspend_bw_hwmon(hw, MON3); } static __always_inline int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); int ret; Loading @@ -711,6 +872,9 @@ static int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) case MON2: handler = bwmon_intr_handler2; break; case MON3: handler = bwmon_intr_handler3; break; } mon_clear(m, false, type); Loading Loading @@ -739,6 +903,11 @@ static int resume_bw_hwmon2(struct bw_hwmon *hw) return __resume_bw_hwmon(hw, MON2); } static int resume_bw_hwmon3(struct bw_hwmon *hw) { return __resume_bw_hwmon(hw, MON3); } /*************************************************************************/ static const struct bwmon_spec spec[] = { Loading @@ -746,25 +915,40 @@ static const struct bwmon_spec spec[] = { .wrap_on_thres = true, .overflow = false, .throt_adj = false, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [1] = { .wrap_on_thres = false, .overflow = true, .throt_adj = false, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [2] = { .wrap_on_thres = false, .overflow = true, .throt_adj = true, .hw_sampling = false .hw_sampling = false, .has_global_base = true, .reg_type = MON1, }, [3] = { .wrap_on_thres = false, .overflow = true, .throt_adj = true, .hw_sampling = true .hw_sampling = true, .has_global_base = true, .reg_type = MON2, }, [4] = { .wrap_on_thres = false, .overflow = true, .throt_adj = false, .hw_sampling = true, .reg_type = MON3, }, }; Loading @@ -773,6 +957,7 @@ static const struct of_device_id bimc_bwmon_match_table[] = { { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, { .compatible = "qcom,bimc-bwmon3", .data = &spec[2] }, { .compatible = "qcom,bimc-bwmon4", .data = &spec[3] }, { .compatible = "qcom,bimc-bwmon5", .data = &spec[4] }, {} }; Loading @@ -789,13 +974,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) 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; m->spec = of_device_get_match_data(dev); if (!m->spec) { dev_err(dev, "Unknown device type!\n"); Loading @@ -813,17 +991,28 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base"); if (m->spec->has_global_base) { 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)); 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; } 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; } m->irq = platform_get_irq(pdev, 0); if (m->irq < 0) { dev_err(dev, "Unable to get IRQ number\n"); Loading @@ -841,20 +1030,33 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) dev_err(dev, "HW sampling rate not specified!\n"); return ret; } } switch (m->spec->reg_type) { case MON3: m->hw.start_hwmon = start_bw_hwmon3; m->hw.stop_hwmon = stop_bw_hwmon3; m->hw.suspend_hwmon = suspend_bw_hwmon3; m->hw.resume_hwmon = resume_bw_hwmon3; m->hw.get_bytes_and_clear = get_bytes_and_clear3; m->hw.set_hw_events = set_hw_events3; break; case MON2: 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 { break; case MON1: 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; break; } if (m->spec->throt_adj) { Loading