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

Commit e7b1a5ee authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "PM / devfreq: bw_hwmon: Add support for BWMON5 monitors" into msm-4.9

parents fecdc1c7 d863ed16
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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
+267 −65
Original line number Diff line number Diff line
@@ -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)
@@ -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 {
@@ -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 {
@@ -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);

@@ -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;
	}
}

@@ -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(),
@@ -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
@@ -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;

@@ -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;
		}
	}
}

@@ -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);
	/*
@@ -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);
	/*
@@ -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;
	}

@@ -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;
	}
}

@@ -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);
@@ -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));
@@ -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;
@@ -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);
@@ -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;
	}

@@ -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;
@@ -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)
{
@@ -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;
@@ -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,
@@ -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);
@@ -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)
{
@@ -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)
{
@@ -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;
@@ -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);
@@ -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[] = {
@@ -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,
	},
};

@@ -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] },
	{}
};

@@ -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");
@@ -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");
@@ -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) {