Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f9a20a7 authored by Rama Aparna Mallavarapu's avatar Rama Aparna Mallavarapu
Browse files

PM / devfreq: bw_hwmon: Add support for BWMON5 monitors



Add support for the fifth type of bandwidth monitor. This monitor
is similar to the other types of monitors, but the register
offset is slightly different, and it doesn't have a global
interrupt base.

Change-Id: Ib05602832b6d4712b783ebaaa5aae9e60c00e30b
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
Signed-off-by: default avatarRama Aparna Mallavarapu <aparnam@codeaurora.org>
parent cc9dd83f
Loading
Loading
Loading
Loading
+269 −65
Original line number Original line Diff line number Diff line
@@ -24,8 +24,12 @@
#define GLB_INT_CLR(m)		((m)->global_base + 0x108)
#define GLB_INT_CLR(m)		((m)->global_base + 0x108)
#define	GLB_INT_EN(m)		((m)->global_base + 0x10C)
#define	GLB_INT_EN(m)		((m)->global_base + 0x10C)
#define MON_INT_STATUS(m)	((m)->base + 0x100)
#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_CLR(m)		((m)->base + 0x108)
#define	MON_INT_EN(m)		((m)->base + 0x10C)
#define	MON_INT_EN(m)		((m)->base + 0x10C)
#define MON_INT_ENABLE		0x1
#define	MON_EN(m)		((m)->base + 0x280)
#define	MON_EN(m)		((m)->base + 0x280)
#define MON_CLEAR(m)		((m)->base + 0x284)
#define MON_CLEAR(m)		((m)->base + 0x284)
#define MON_CNT(m)		((m)->base + 0x288)
#define MON_CNT(m)		((m)->base + 0x288)
@@ -46,9 +50,27 @@
#define MON2_ZONE_CNT(m)	((m)->base + 0x2D8)
#define MON2_ZONE_CNT(m)	((m)->base + 0x2D8)
#define MON2_ZONE_MAX(m, zone)	((m)->base + 0x2E0 + 0x4 * zone)
#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 {
enum mon_reg_type {
	MON1,
	MON1,
	MON2,
	MON2,
	MON3,
};
};


struct bwmon_spec {
struct bwmon_spec {
@@ -56,6 +78,8 @@ struct bwmon_spec {
	bool overflow;
	bool overflow;
	bool throt_adj;
	bool throt_adj;
	bool hw_sampling;
	bool hw_sampling;
	bool has_global_base;
	enum mon_reg_type reg_type;
};
};


struct bwmon {
struct bwmon {
@@ -77,9 +101,6 @@ struct bwmon {
#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);


@@ -92,6 +113,9 @@ static __always_inline void mon_enable(struct bwmon *m, enum mon_reg_type type)
	case MON2:
	case MON2:
		writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m));
		writel_relaxed(ENABLE_MASK | m->throttle_adj, MON2_EN(m));
		break;
		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:
	case MON2:
		writel_relaxed(m->throttle_adj, MON2_EN(m));
		writel_relaxed(m->throttle_adj, MON2_EN(m));
		break;
		break;
	case MON3:
		writel_relaxed(m->throttle_adj, MON3_EN(m));
		break;
	}
	}
	/*
	/*
	 * mon_disable() and mon_irq_clear(),
	 * 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
		else
			writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m));
			writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m));
		break;
		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
	 * 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
#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;
	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",
			pr_warn("Sample window %u larger than hw limit: %u\n",
					rate, SAMPLE_WIN_LIM);
					rate, SAMPLE_WIN_LIM);
		}
		}
		switch (type) {
		case MON1:
			WARN(1, "Invalid\n");
			return;
		case MON2:
			writel_relaxed(rate, MON2_SW(m));
			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:
	case MON1:
		mon_glb_irq_enable(m);
		mon_glb_irq_enable(m);
		val = readl_relaxed(MON_INT_EN(m));
		val = readl_relaxed(MON_INT_EN(m));
		val |= INT_ENABLE_V1;
		val |= MON_INT_ENABLE;
		writel_relaxed(val, MON_INT_EN(m));
		writel_relaxed(val, MON_INT_EN(m));
		break;
		break;
	case MON2:
	case MON2:
		mon_glb_irq_enable(m);
		mon_glb_irq_enable(m);
		val = readl_relaxed(MON_INT_EN(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));
		writel_relaxed(val, MON_INT_EN(m));
		break;
		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);
	spin_unlock(&glb_lock);
	/*
	/*
@@ -211,15 +261,20 @@ void mon_irq_disable(struct bwmon *m, enum mon_reg_type type)
	case MON1:
	case MON1:
		mon_glb_irq_disable(m);
		mon_glb_irq_disable(m);
		val = readl_relaxed(MON_INT_EN(m));
		val = readl_relaxed(MON_INT_EN(m));
		val &= ~INT_ENABLE_V1;
		val &= ~MON_INT_ENABLE;
		writel_relaxed(val, MON_INT_EN(m));
		writel_relaxed(val, MON_INT_EN(m));
		break;
		break;
	case MON2:
	case MON2:
		mon_glb_irq_disable(m);
		mon_glb_irq_disable(m);
		val = readl_relaxed(MON_INT_EN(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));
		writel_relaxed(val, MON_INT_EN(m));
		break;
		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);
	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));
		mval = readl_relaxed(MON_INT_STATUS(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 &= INT_STATUS_MASK;
		mval &= MON_INT_STATUS_MASK;
		break;
		break;
	case MON2:
	case MON2:
		mval = readl_relaxed(MON_INT_STATUS(m));
		mval = readl_relaxed(MON_INT_STATUS(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 &= 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;
		break;
	}
	}


@@ -279,13 +340,16 @@ void mon_irq_clear(struct bwmon *m, enum mon_reg_type type)
{
{
	switch (type) {
	switch (type) {
	case MON1:
	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);
		mon_glb_irq_clear(m);
		break;
		break;
	case MON2:
	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);
		mon_glb_irq_clear(m);
		break;
		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
 * Zone 3: byte count > THRES_HI
 */
 */
#define	THRES_LIM	0x7FFU
#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 hi, med, lo;
	u32 zone_cnt_thres = calc_zone_counts(hw);


	hi = mbps_to_mb(hw->up_wake_mbps, sample_ms);
	hi = mbps_to_mb(hw->up_wake_mbps, sample_ms);
	med = mbps_to_mb(hw->down_wake_mbps, sample_ms);
	med = mbps_to_mb(hw->down_wake_mbps, sample_ms);
@@ -374,25 +441,38 @@ static void set_zone_thres(struct bwmon *m, unsigned int sample_ms)
		lo = min(lo, med-1);
		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(hi, MON2_THRES_HI(m));
		writel_relaxed(med, MON2_THRES_MED(m));
		writel_relaxed(med, MON2_THRES_MED(m));
		writel_relaxed(lo, MON2_THRES_LO(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 */
		/* Set the zone count thresholds for interrupts */
		writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m));
		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);
	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)
static void mon_set_limit(struct bwmon *m, u32 count)
{
{
	writel_relaxed(count, MON_THRES(m));
	writel_relaxed(count, MON_THRES(m));
@@ -425,16 +505,28 @@ static unsigned long mon_get_count1(struct bwmon *m)
	return count;
	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_counts;
	u32 zone;
	u32 zone;


	zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4);
	zone = get_bitmask_order(m->intr_status);
	if (zone) {
	if (zone) {
		zone--;
		zone--;
	} else {
	} else {
		switch (type) {
		case MON1:
			WARN(1, "Invalid\n");
			return 0;
		case MON2:
			zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
			zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
			break;
		case MON3:
			zone_counts = readl_relaxed(MON3_ZONE_CNT(m));
			break;
		}

		if (zone_counts) {
		if (zone_counts) {
			zone = get_bitmask_order(zone_counts) - 1;
			zone = get_bitmask_order(zone_counts) - 1;
			zone /= 8;
			zone /= 8;
@@ -445,14 +537,37 @@ static unsigned int get_zone(struct bwmon *m)
	return zone;
	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 int zone;
	unsigned long count = 0;
	unsigned long count = 0;


	zone = get_zone(m);
	zone = get_zone(m, type);

	count = get_zone_count(m, zone, type);
	count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
	count *= SZ_1M;
	count *= SZ_1M;


	dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count);
	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);
		count = mon_get_count1(m);
		break;
		break;
	case MON2:
	case MON2:
		count = mon_get_zone_stats(m);
	case MON3:
		count = mon_get_zone_stats(m, type);
		break;
		break;
	}
	}


@@ -515,6 +631,11 @@ static unsigned long get_bytes_and_clear2(struct bw_hwmon *hw)
	return __get_bytes_and_clear(hw, MON2);
	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)
static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
{
{
	unsigned long count;
	unsigned long count;
@@ -537,20 +658,33 @@ 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 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);
	struct bwmon *m = to_bwmon(hw);


	mon_disable(m, MON2);
	mon_disable(m, type);
	mon_clear(m, false, MON2);
	mon_clear(m, false, type);
	mon_irq_clear(m, MON2);
	mon_irq_clear(m, type);


	mon_set_zones(m, sample_ms);
	mon_set_zones(m, sample_ms, type);
	mon_enable(m, MON2);
	mon_enable(m, type);


	return 0;
	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
static irqreturn_t
__bwmon_intr_handler(int irq, void *dev, enum mon_reg_type type)
__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);
	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)
static irqreturn_t bwmon_intr_thread(int irq, void *dev)
{
{
	struct bwmon *m = 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();
		zone_actions = calc_zone_actions();
		handler = bwmon_intr_handler2;
		handler = bwmon_intr_handler2;
		break;
		break;
	case MON3:
		zone_actions = calc_zone_actions();
		handler = bwmon_intr_handler3;
		break;
	}
	}


	ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread,
	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);
		mon_set_limit(m, limit);
		break;
		break;
	case MON2:
	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 */
		/* Set the zone actions to increment appropriate counters */
		writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m));
		writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m));
		break;
		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);
	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);
	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
static __always_inline
void __stop_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
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);
	return __stop_bw_hwmon(hw, MON2);
}
}


static void stop_bw_hwmon3(struct bw_hwmon *hw)
{
	return __stop_bw_hwmon(hw, MON3);
}

static __always_inline
static __always_inline
int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
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);
	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);
	struct bwmon *m = to_bwmon(hw);
	int ret;
	int ret;
@@ -702,6 +865,9 @@ static int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type)
	case MON2:
	case MON2:
		handler = bwmon_intr_handler2;
		handler = bwmon_intr_handler2;
		break;
		break;
	case MON3:
		handler = bwmon_intr_handler3;
		break;
	}
	}


	mon_clear(m, false, type);
	mon_clear(m, false, type);
@@ -730,6 +896,11 @@ static int resume_bw_hwmon2(struct bw_hwmon *hw)
	return __resume_bw_hwmon(hw, MON2);
	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[] = {
static const struct bwmon_spec spec[] = {
@@ -737,25 +908,40 @@ static const struct bwmon_spec spec[] = {
		.wrap_on_thres = true,
		.wrap_on_thres = true,
		.overflow = false,
		.overflow = false,
		.throt_adj = false,
		.throt_adj = false,
		.hw_sampling = false
		.hw_sampling = false,
		.has_global_base = true,
		.reg_type = MON1,
	},
	},
	[1] = {
	[1] = {
		.wrap_on_thres = false,
		.wrap_on_thres = false,
		.overflow = true,
		.overflow = true,
		.throt_adj = false,
		.throt_adj = false,
		.hw_sampling = false
		.hw_sampling = false,
		.has_global_base = true,
		.reg_type = MON1,
	},
	},
	[2] = {
	[2] = {
		.wrap_on_thres = false,
		.wrap_on_thres = false,
		.overflow = true,
		.overflow = true,
		.throt_adj = true,
		.throt_adj = true,
		.hw_sampling = false
		.hw_sampling = false,
		.has_global_base = true,
		.reg_type = MON1,
	},
	},
	[3] = {
	[3] = {
		.wrap_on_thres = false,
		.wrap_on_thres = false,
		.overflow = true,
		.overflow = true,
		.throt_adj = 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-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] },
	{ .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;
		return -ENOMEM;
	m->dev = dev;
	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);
	m->spec = of_device_get_match_data(dev);
	if (!m->spec) {
	if (!m->spec) {
		dev_err(dev, "Unknown device type!\n");
		dev_err(dev, "Unknown device type!\n");
@@ -804,17 +984,28 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
		return -ENOMEM;
		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) {
		if (!res) {
			dev_err(dev, "global_base not found!\n");
			dev_err(dev, "global_base not found!\n");
			return -EINVAL;
			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) {
		if (!m->global_base) {
			dev_err(dev, "Unable map global_base!\n");
			dev_err(dev, "Unable map global_base!\n");
			return -ENOMEM;
			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);
	m->irq = platform_get_irq(pdev, 0);
	if (m->irq < 0) {
	if (m->irq < 0) {
		dev_err(dev, "Unable to get IRQ number\n");
		dev_err(dev, "Unable to get IRQ number\n");
@@ -832,20 +1023,33 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
			dev_err(dev, "HW sampling rate not specified!\n");
			dev_err(dev, "HW sampling rate not specified!\n");
			return ret;
			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.start_hwmon = start_bw_hwmon2;
		m->hw.stop_hwmon = stop_bw_hwmon2;
		m->hw.stop_hwmon = stop_bw_hwmon2;
		m->hw.suspend_hwmon = suspend_bw_hwmon2;
		m->hw.suspend_hwmon = suspend_bw_hwmon2;
		m->hw.resume_hwmon = resume_bw_hwmon2;
		m->hw.resume_hwmon = resume_bw_hwmon2;
		m->hw.get_bytes_and_clear = get_bytes_and_clear2;
		m->hw.get_bytes_and_clear = get_bytes_and_clear2;
		m->hw.set_hw_events = set_hw_events;
		m->hw.set_hw_events = set_hw_events;
	} else {
		break;
	case MON1:
		m->hw.start_hwmon = start_bw_hwmon;
		m->hw.start_hwmon = start_bw_hwmon;
		m->hw.stop_hwmon = stop_bw_hwmon;
		m->hw.stop_hwmon = stop_bw_hwmon;
		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;
		break;
	}
	}


	if (m->spec->throt_adj) {
	if (m->spec->throt_adj) {