diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 5a4305822993..742cbcf9d349 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -24,8 +24,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) @@ -46,9 +50,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 { @@ -56,6 +78,8 @@ struct bwmon_spec { bool overflow; bool throt_adj; bool hw_sampling; + bool has_global_base; + enum mon_reg_type reg_type; }; struct bwmon { @@ -77,9 +101,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); @@ -92,6 +113,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; } } @@ -104,6 +128,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(), @@ -128,6 +155,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 @@ -138,7 +171,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; @@ -150,7 +185,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); } - writel_relaxed(rate, MON2_SW(m)); + 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; + } } } @@ -173,15 +218,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); /* @@ -211,15 +261,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); /* @@ -239,13 +294,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; } @@ -279,13 +340,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; } } @@ -357,10 +421,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); @@ -374,23 +441,36 @@ static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) lo = min(lo, med-1); } - writel_relaxed(hi, MON2_THRES_HI(m)); - writel_relaxed(med, MON2_THRES_MED(m)); - writel_relaxed(lo, MON2_THRES_LO(m)); + 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)); + /* 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 void mon_set_zones(struct bwmon *m, unsigned int sample_ms) +static __always_inline +void mon_set_zones(struct bwmon *m, unsigned int sample_ms, + enum mon_reg_type type) { - 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)); - - dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres); + 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) @@ -425,16 +505,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 { - zone_counts = readl_relaxed(MON2_ZONE_CNT(m)); + 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; @@ -445,14 +537,37 @@ 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)); + if (count) + count++; + 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); @@ -470,7 +585,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; } @@ -515,6 +631,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; @@ -537,20 +658,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) { @@ -576,6 +710,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; @@ -601,6 +740,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, @@ -621,10 +764,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); @@ -644,6 +791,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) { @@ -666,6 +818,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) { @@ -689,7 +846,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; @@ -702,6 +865,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); @@ -730,6 +896,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[] = { @@ -737,25 +908,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, }, }; @@ -764,6 +950,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] }, {} }; @@ -780,13 +967,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 < 0) { - dev_err(dev, "mport not found! (%d)\n", ret); - return ret; - } - m->mport = data; - m->spec = of_device_get_match_data(dev); if (!m->spec) { dev_err(dev, "Unknown device type!\n"); @@ -804,15 +984,26 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) 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; + 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)); + 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 < 0) { + dev_err(dev, "mport not found! (%d)\n", ret); + return ret; + } + m->mport = data; } m->irq = platform_get_irq(pdev, 0); @@ -832,20 +1023,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) {