Merge "ARM: dts: msm: enable bwmon4 for cpubw monitor for msmcobalt"
This commit is contained in:
commit
7ba1c55052
3 changed files with 251 additions and 25 deletions
|
@ -5,8 +5,8 @@ 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.
|
master ports. For example, the CPU subsystem sits on one BIMC master port.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" or
|
- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2",
|
||||||
"qcom,bimc-bwmon3"
|
"qcom,bimc-bwmon3" or "qcom,bimc-bwmon4"
|
||||||
- reg: Pairs of physical base addresses and region sizes of
|
- reg: Pairs of physical base addresses and region sizes of
|
||||||
memory mapped registers.
|
memory mapped registers.
|
||||||
- reg-names: Names of the bases for the above registers. Expected
|
- reg-names: Names of the bases for the above registers. Expected
|
||||||
|
@ -14,6 +14,8 @@ Required properties:
|
||||||
- interrupts: Lists the threshold IRQ.
|
- interrupts: Lists the threshold IRQ.
|
||||||
- qcom,mport: The hardware master port that this device can monitor
|
- qcom,mport: The hardware master port that this device can monitor
|
||||||
- qcom,target-dev: The DT device that corresponds to this master port
|
- qcom,target-dev: The DT device that corresponds to this master port
|
||||||
|
- qcom,hw-timer-hz: Hardware sampling rate in Hz. This field must be
|
||||||
|
specified for "qcom,bimc-bwmon4"
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
qcom,cpu-bwmon {
|
qcom,cpu-bwmon {
|
||||||
|
@ -23,4 +25,5 @@ Example:
|
||||||
interrupts = <0 183 1>;
|
interrupts = <0 183 1>;
|
||||||
qcom,mport = <0>;
|
qcom,mport = <0>;
|
||||||
qcom,target-dev = <&cpubw>;
|
qcom,target-dev = <&cpubw>;
|
||||||
|
qcom,hw-timer-hz = <19200000>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -542,12 +542,13 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
qcom,cpu-bwmon {
|
qcom,cpu-bwmon {
|
||||||
compatible = "qcom,bimc-bwmon3";
|
compatible = "qcom,bimc-bwmon4";
|
||||||
reg = <0x01008000 0x300>, <0x01001000 0x200>;
|
reg = <0x01008000 0x300>, <0x01001000 0x200>;
|
||||||
reg-names = "base", "global_base";
|
reg-names = "base", "global_base";
|
||||||
interrupts = <0 183 4>;
|
interrupts = <0 183 4>;
|
||||||
qcom,mport = <0>;
|
qcom,mport = <0>;
|
||||||
qcom,target-dev = <&cpubw>;
|
qcom,target-dev = <&cpubw>;
|
||||||
|
qcom,hw-timer-hz = <19200000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
mincpubw: qcom,mincpubw {
|
mincpubw: qcom,mincpubw {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -40,10 +41,24 @@
|
||||||
#define MON_MASK(m) ((m)->base + 0x298)
|
#define MON_MASK(m) ((m)->base + 0x298)
|
||||||
#define MON_MATCH(m) ((m)->base + 0x29C)
|
#define MON_MATCH(m) ((m)->base + 0x29C)
|
||||||
|
|
||||||
|
#define MON2_EN(m) ((m)->base + 0x2A0)
|
||||||
|
#define MON2_CLEAR(m) ((m)->base + 0x2A4)
|
||||||
|
#define MON2_SW(m) ((m)->base + 0x2A8)
|
||||||
|
#define MON2_THRES_HI(m) ((m)->base + 0x2AC)
|
||||||
|
#define MON2_THRES_MED(m) ((m)->base + 0x2B0)
|
||||||
|
#define MON2_THRES_LO(m) ((m)->base + 0x2B4)
|
||||||
|
#define MON2_ZONE_ACTIONS(m) ((m)->base + 0x2B8)
|
||||||
|
#define MON2_ZONE_CNT_THRES(m) ((m)->base + 0x2BC)
|
||||||
|
#define MON2_BYTE_CNT(m) ((m)->base + 0x2D0)
|
||||||
|
#define MON2_WIN_TIMER(m) ((m)->base + 0x2D4)
|
||||||
|
#define MON2_ZONE_CNT(m) ((m)->base + 0x2D8)
|
||||||
|
#define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone)
|
||||||
|
|
||||||
struct bwmon_spec {
|
struct bwmon_spec {
|
||||||
bool wrap_on_thres;
|
bool wrap_on_thres;
|
||||||
bool overflow;
|
bool overflow;
|
||||||
bool throt_adj;
|
bool throt_adj;
|
||||||
|
bool hw_sampling;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bwmon {
|
struct bwmon {
|
||||||
|
@ -54,24 +69,37 @@ struct bwmon {
|
||||||
const struct bwmon_spec *spec;
|
const struct bwmon_spec *spec;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct bw_hwmon hw;
|
struct bw_hwmon hw;
|
||||||
|
u32 hw_timer_hz;
|
||||||
u32 throttle_adj;
|
u32 throttle_adj;
|
||||||
|
u32 sample_size_ms;
|
||||||
|
u32 intr_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_bwmon(ptr) container_of(ptr, struct bwmon, hw)
|
#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 ENABLE_MASK BIT(0)
|
||||||
#define THROTTLE_MASK 0x1F
|
#define THROTTLE_MASK 0x1F
|
||||||
#define THROTTLE_SHIFT 16
|
#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);
|
static DEFINE_SPINLOCK(glb_lock);
|
||||||
static void mon_enable(struct bwmon *m)
|
static void mon_enable(struct bwmon *m)
|
||||||
{
|
{
|
||||||
writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mon_disable(struct bwmon *m)
|
static void mon_disable(struct bwmon *m)
|
||||||
{
|
{
|
||||||
writel_relaxed(m->throttle_adj, MON_EN(m));
|
if (has_hw_sampling(m))
|
||||||
|
writel_relaxed(m->throttle_adj, MON2_EN(m));
|
||||||
|
else
|
||||||
|
writel_relaxed(m->throttle_adj, MON_EN(m));
|
||||||
/*
|
/*
|
||||||
* mon_disable() and mon_irq_clear(),
|
* mon_disable() and mon_irq_clear(),
|
||||||
* If latter goes first and count happen to trigger irq, we would
|
* If latter goes first and count happen to trigger irq, we would
|
||||||
|
@ -80,17 +108,46 @@ static void mon_disable(struct bwmon *m)
|
||||||
mb();
|
mb();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mon_clear(struct bwmon *m)
|
#define MON_CLEAR_BIT 0x1
|
||||||
|
#define MON_CLEAR_ALL_BIT 0x2
|
||||||
|
static void mon_clear(struct bwmon *m, bool clear_all)
|
||||||
{
|
{
|
||||||
writel_relaxed(0x1, MON_CLEAR(m));
|
if (!has_hw_sampling(m)) {
|
||||||
|
writel_relaxed(MON_CLEAR_BIT, MON_CLEAR(m));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* 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
|
* 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.
|
* before we try to clear the IRQ or do any other counter operations.
|
||||||
*/
|
*/
|
||||||
|
out:
|
||||||
mb();
|
mb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SAMPLE_WIN_LIM 0xFFFFF
|
||||||
|
static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms)
|
||||||
|
{
|
||||||
|
u32 rate;
|
||||||
|
|
||||||
|
if (unlikely(sample_ms != m->sample_size_ms)) {
|
||||||
|
rate = mult_frac(sample_ms, m->hw_timer_hz, MSEC_PER_SEC);
|
||||||
|
m->sample_size_ms = sample_ms;
|
||||||
|
if (unlikely(rate > SAMPLE_WIN_LIM)) {
|
||||||
|
rate = SAMPLE_WIN_LIM;
|
||||||
|
pr_warn("Sample window %u larger than hw limit: %u\n",
|
||||||
|
rate, SAMPLE_WIN_LIM);
|
||||||
|
}
|
||||||
|
writel_relaxed(rate, MON2_SW(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mon_irq_enable(struct bwmon *m)
|
static void mon_irq_enable(struct bwmon *m)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -99,11 +156,11 @@ static void mon_irq_enable(struct bwmon *m)
|
||||||
val = readl_relaxed(GLB_INT_EN(m));
|
val = readl_relaxed(GLB_INT_EN(m));
|
||||||
val |= 1 << m->mport;
|
val |= 1 << m->mport;
|
||||||
writel_relaxed(val, GLB_INT_EN(m));
|
writel_relaxed(val, GLB_INT_EN(m));
|
||||||
spin_unlock(&glb_lock);
|
|
||||||
|
|
||||||
val = readl_relaxed(MON_INT_EN(m));
|
val = readl_relaxed(MON_INT_EN(m));
|
||||||
val |= 0x1;
|
val |= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_ENABLE_V1;
|
||||||
writel_relaxed(val, MON_INT_EN(m));
|
writel_relaxed(val, MON_INT_EN(m));
|
||||||
|
spin_unlock(&glb_lock);
|
||||||
/*
|
/*
|
||||||
* make Sure irq enable complete for local and global
|
* make Sure irq enable complete for local and global
|
||||||
* to avoid race with other monitor calls
|
* to avoid race with other monitor calls
|
||||||
|
@ -119,11 +176,11 @@ static void mon_irq_disable(struct bwmon *m)
|
||||||
val = readl_relaxed(GLB_INT_EN(m));
|
val = readl_relaxed(GLB_INT_EN(m));
|
||||||
val &= ~(1 << m->mport);
|
val &= ~(1 << m->mport);
|
||||||
writel_relaxed(val, GLB_INT_EN(m));
|
writel_relaxed(val, GLB_INT_EN(m));
|
||||||
spin_unlock(&glb_lock);
|
|
||||||
|
|
||||||
val = readl_relaxed(MON_INT_EN(m));
|
val = readl_relaxed(MON_INT_EN(m));
|
||||||
val &= ~0x1;
|
val &= has_hw_sampling(m) ? ~INT_STATUS_MASK_HWS : ~INT_ENABLE_V1;
|
||||||
writel_relaxed(val, MON_INT_EN(m));
|
writel_relaxed(val, MON_INT_EN(m));
|
||||||
|
spin_unlock(&glb_lock);
|
||||||
/*
|
/*
|
||||||
* make Sure irq disable complete for local and global
|
* make Sure irq disable complete for local and global
|
||||||
* to avoid race with other monitor calls
|
* to avoid race with other monitor calls
|
||||||
|
@ -140,12 +197,18 @@ static unsigned int mon_irq_status(struct bwmon *m)
|
||||||
dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
|
dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
|
||||||
readl_relaxed(GLB_INT_STATUS(m)));
|
readl_relaxed(GLB_INT_STATUS(m)));
|
||||||
|
|
||||||
|
mval &= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK;
|
||||||
|
|
||||||
return mval;
|
return mval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mon_irq_clear(struct bwmon *m)
|
static void mon_irq_clear(struct bwmon *m)
|
||||||
{
|
{
|
||||||
writel_relaxed(0x3, MON_INT_CLR(m));
|
u32 intclr;
|
||||||
|
|
||||||
|
intclr = has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK;
|
||||||
|
|
||||||
|
writel_relaxed(intclr, MON_INT_CLR(m));
|
||||||
mb();
|
mb();
|
||||||
writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
|
writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
|
||||||
mb();
|
mb();
|
||||||
|
@ -171,6 +234,90 @@ static u32 mon_get_throttle_adj(struct bw_hwmon *hw)
|
||||||
return m->throttle_adj >> THROTTLE_SHIFT;
|
return m->throttle_adj >> THROTTLE_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ZONE1_SHIFT 8
|
||||||
|
#define ZONE2_SHIFT 16
|
||||||
|
#define ZONE3_SHIFT 24
|
||||||
|
#define ZONE0_ACTION 0x01 /* Increment zone 0 count */
|
||||||
|
#define ZONE1_ACTION 0x09 /* Increment zone 1 & clear lower zones */
|
||||||
|
#define ZONE2_ACTION 0x25 /* Increment zone 2 & clear lower zones */
|
||||||
|
#define ZONE3_ACTION 0x95 /* Increment zone 3 & clear lower zones */
|
||||||
|
static u32 calc_zone_actions(void)
|
||||||
|
{
|
||||||
|
u32 zone_actions;
|
||||||
|
|
||||||
|
zone_actions = ZONE0_ACTION;
|
||||||
|
zone_actions |= ZONE1_ACTION << ZONE1_SHIFT;
|
||||||
|
zone_actions |= ZONE2_ACTION << ZONE2_SHIFT;
|
||||||
|
zone_actions |= ZONE3_ACTION << ZONE3_SHIFT;
|
||||||
|
|
||||||
|
return zone_actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZONE_CNT_LIM 0xFFU
|
||||||
|
#define UP_CNT_1 1
|
||||||
|
static u32 calc_zone_counts(struct bw_hwmon *hw)
|
||||||
|
{
|
||||||
|
u32 zone_counts;
|
||||||
|
|
||||||
|
zone_counts = ZONE_CNT_LIM;
|
||||||
|
zone_counts |= min(hw->down_cnt, ZONE_CNT_LIM) << ZONE1_SHIFT;
|
||||||
|
zone_counts |= ZONE_CNT_LIM << ZONE2_SHIFT;
|
||||||
|
zone_counts |= UP_CNT_1 << ZONE3_SHIFT;
|
||||||
|
|
||||||
|
return zone_counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms)
|
||||||
|
{
|
||||||
|
mbps *= ms;
|
||||||
|
mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC);
|
||||||
|
return mbps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define the 4 zones using HI, MED & LO thresholds:
|
||||||
|
* Zone 0: byte count < THRES_LO
|
||||||
|
* Zone 1: THRES_LO < byte count < THRES_MED
|
||||||
|
* Zone 2: THRES_MED < byte count < THRES_HI
|
||||||
|
* Zone 3: byte count > THRES_HI
|
||||||
|
*/
|
||||||
|
#define THRES_LIM 0x7FFU
|
||||||
|
static void set_zone_thres(struct bwmon *m, unsigned int sample_ms)
|
||||||
|
{
|
||||||
|
struct bw_hwmon *hw = &(m->hw);
|
||||||
|
u32 hi, med, lo;
|
||||||
|
|
||||||
|
hi = mbps_to_mb(hw->up_wake_mbps, sample_ms);
|
||||||
|
med = mbps_to_mb(hw->down_wake_mbps, sample_ms);
|
||||||
|
lo = 0;
|
||||||
|
|
||||||
|
if (unlikely((hi > THRES_LIM) || (med > hi) || (lo > med))) {
|
||||||
|
pr_warn("Zone thres larger than hw limit: hi:%u med:%u lo:%u\n",
|
||||||
|
hi, med, lo);
|
||||||
|
hi = min(hi, THRES_LIM);
|
||||||
|
med = min(med, hi - 1);
|
||||||
|
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));
|
||||||
|
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));
|
||||||
|
|
||||||
|
dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres);
|
||||||
|
}
|
||||||
|
|
||||||
static void mon_set_limit(struct bwmon *m, u32 count)
|
static void mon_set_limit(struct bwmon *m, u32 count)
|
||||||
{
|
{
|
||||||
writel_relaxed(count, MON_THRES(m));
|
writel_relaxed(count, MON_THRES(m));
|
||||||
|
@ -203,6 +350,41 @@ static unsigned long mon_get_count(struct bwmon *m)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int get_zone(struct bwmon *m)
|
||||||
|
{
|
||||||
|
u32 zone_counts;
|
||||||
|
u32 zone;
|
||||||
|
|
||||||
|
zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4);
|
||||||
|
if (zone) {
|
||||||
|
zone--;
|
||||||
|
} else {
|
||||||
|
zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
|
||||||
|
if (zone_counts) {
|
||||||
|
zone = get_bitmask_order(zone_counts) - 1;
|
||||||
|
zone /= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m->intr_status = 0;
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long mon_get_zone_stats(struct bwmon *m)
|
||||||
|
{
|
||||||
|
unsigned int zone;
|
||||||
|
unsigned long count = 0;
|
||||||
|
|
||||||
|
zone = get_zone(m);
|
||||||
|
|
||||||
|
count = readl_relaxed(MON2_ZONE_MAX(m, zone));
|
||||||
|
count *= SZ_1M;
|
||||||
|
|
||||||
|
dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********** CPUBW specific code ********** */
|
/* ********** CPUBW specific code ********** */
|
||||||
|
|
||||||
/* Returns MBps of read/writes for the sampling window. */
|
/* Returns MBps of read/writes for the sampling window. */
|
||||||
|
@ -222,8 +404,8 @@ static unsigned long get_bytes_and_clear(struct bw_hwmon *hw)
|
||||||
unsigned long count;
|
unsigned long count;
|
||||||
|
|
||||||
mon_disable(m);
|
mon_disable(m);
|
||||||
count = mon_get_count(m);
|
count = has_hw_sampling(m) ? mon_get_zone_stats(m) : mon_get_count(m);
|
||||||
mon_clear(m);
|
mon_clear(m, false);
|
||||||
mon_irq_clear(m);
|
mon_irq_clear(m);
|
||||||
mon_enable(m);
|
mon_enable(m);
|
||||||
|
|
||||||
|
@ -238,7 +420,7 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
|
||||||
|
|
||||||
mon_disable(m);
|
mon_disable(m);
|
||||||
count = mon_get_count(m);
|
count = mon_get_count(m);
|
||||||
mon_clear(m);
|
mon_clear(m, false);
|
||||||
mon_irq_clear(m);
|
mon_irq_clear(m);
|
||||||
|
|
||||||
if (likely(!m->spec->wrap_on_thres))
|
if (likely(!m->spec->wrap_on_thres))
|
||||||
|
@ -252,11 +434,26 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned sample_ms)
|
||||||
|
{
|
||||||
|
struct bwmon *m = to_bwmon(hw);
|
||||||
|
|
||||||
|
mon_disable(m);
|
||||||
|
mon_clear(m, false);
|
||||||
|
mon_irq_clear(m);
|
||||||
|
|
||||||
|
mon_set_zones(m, sample_ms);
|
||||||
|
mon_enable(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t bwmon_intr_handler(int irq, void *dev)
|
static irqreturn_t bwmon_intr_handler(int irq, void *dev)
|
||||||
{
|
{
|
||||||
struct bwmon *m = dev;
|
struct bwmon *m = dev;
|
||||||
|
|
||||||
if (!mon_irq_status(m))
|
m->intr_status = mon_irq_status(m);
|
||||||
|
if (!m->intr_status)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
if (bw_hwmon_sample_end(&m->hw) > 0)
|
if (bw_hwmon_sample_end(&m->hw) > 0)
|
||||||
|
@ -277,6 +474,7 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
|
||||||
{
|
{
|
||||||
struct bwmon *m = to_bwmon(hw);
|
struct bwmon *m = to_bwmon(hw);
|
||||||
u32 limit;
|
u32 limit;
|
||||||
|
u32 zone_actions = calc_zone_actions();
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
|
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
|
||||||
|
@ -291,10 +489,16 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
|
||||||
|
|
||||||
mon_disable(m);
|
mon_disable(m);
|
||||||
|
|
||||||
|
mon_clear(m, true);
|
||||||
limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0);
|
limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0);
|
||||||
mon_set_limit(m, limit);
|
if (has_hw_sampling(m)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
mon_clear(m);
|
|
||||||
mon_irq_clear(m);
|
mon_irq_clear(m);
|
||||||
mon_irq_enable(m);
|
mon_irq_enable(m);
|
||||||
mon_enable(m);
|
mon_enable(m);
|
||||||
|
@ -309,7 +513,7 @@ static void stop_bw_hwmon(struct bw_hwmon *hw)
|
||||||
mon_irq_disable(m);
|
mon_irq_disable(m);
|
||||||
free_irq(m->irq, m);
|
free_irq(m->irq, m);
|
||||||
mon_disable(m);
|
mon_disable(m);
|
||||||
mon_clear(m);
|
mon_clear(m, true);
|
||||||
mon_irq_clear(m);
|
mon_irq_clear(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +534,7 @@ static int resume_bw_hwmon(struct bw_hwmon *hw)
|
||||||
struct bwmon *m = to_bwmon(hw);
|
struct bwmon *m = to_bwmon(hw);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mon_clear(m);
|
mon_clear(m, false);
|
||||||
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
|
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
|
||||||
bwmon_intr_thread,
|
bwmon_intr_thread,
|
||||||
IRQF_ONESHOT | IRQF_SHARED,
|
IRQF_ONESHOT | IRQF_SHARED,
|
||||||
|
@ -350,15 +554,21 @@ static int resume_bw_hwmon(struct bw_hwmon *hw)
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
static const struct bwmon_spec spec[] = {
|
static const struct bwmon_spec spec[] = {
|
||||||
{ .wrap_on_thres = true, .overflow = false, .throt_adj = false},
|
{ .wrap_on_thres = true, .overflow = false, .throt_adj = false,
|
||||||
{ .wrap_on_thres = false, .overflow = true, .throt_adj = false},
|
.hw_sampling = false},
|
||||||
{ .wrap_on_thres = false, .overflow = true, .throt_adj = true},
|
{ .wrap_on_thres = false, .overflow = true, .throt_adj = false,
|
||||||
|
.hw_sampling = false},
|
||||||
|
{ .wrap_on_thres = false, .overflow = true, .throt_adj = true,
|
||||||
|
.hw_sampling = false},
|
||||||
|
{ .wrap_on_thres = false, .overflow = true, .throt_adj = true,
|
||||||
|
.hw_sampling = true},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct of_device_id match_table[] = {
|
static struct of_device_id match_table[] = {
|
||||||
{ .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
|
{ .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
|
||||||
{ .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
|
{ .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
|
||||||
{ .compatible = "qcom,bimc-bwmon3", .data = &spec[2] },
|
{ .compatible = "qcom,bimc-bwmon3", .data = &spec[2] },
|
||||||
|
{ .compatible = "qcom,bimc-bwmon4", .data = &spec[3] },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -390,6 +600,16 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
m->spec = id->data;
|
m->spec = id->data;
|
||||||
|
|
||||||
|
if (has_hw_sampling(m)) {
|
||||||
|
ret = of_property_read_u32(dev->of_node,
|
||||||
|
"qcom,hw-timer-hz", &data);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "HW sampling rate not specified!\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
m->hw_timer_hz = data;
|
||||||
|
}
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
|
||||||
if (!res) {
|
if (!res) {
|
||||||
dev_err(dev, "base not found!\n");
|
dev_err(dev, "base not found!\n");
|
||||||
|
@ -426,7 +646,9 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
|
||||||
m->hw.suspend_hwmon = &suspend_bw_hwmon;
|
m->hw.suspend_hwmon = &suspend_bw_hwmon;
|
||||||
m->hw.resume_hwmon = &resume_bw_hwmon;
|
m->hw.resume_hwmon = &resume_bw_hwmon;
|
||||||
m->hw.get_bytes_and_clear = &get_bytes_and_clear;
|
m->hw.get_bytes_and_clear = &get_bytes_and_clear;
|
||||||
m->hw.set_thres = &set_thres;
|
m->hw.set_thres = &set_thres;
|
||||||
|
if (has_hw_sampling(m))
|
||||||
|
m->hw.set_hw_events = &set_hw_events;
|
||||||
if (m->spec->throt_adj) {
|
if (m->spec->throt_adj) {
|
||||||
m->hw.set_throttle_adj = &mon_set_throttle_adj;
|
m->hw.set_throttle_adj = &mon_set_throttle_adj;
|
||||||
m->hw.get_throttle_adj = &mon_get_throttle_adj;
|
m->hw.get_throttle_adj = &mon_get_throttle_adj;
|
||||||
|
|
Loading…
Add table
Reference in a new issue