static void stop_monitoring(void)
{
	global_mon_enable(false);
	mon_disable(0);
	mon_disable(1);
	set_l2_indirect_reg(L2PMINTENCLR, BIT(0));
	set_l2_indirect_reg(L2PMINTENCLR, BIT(1));

	disable_irq(MON_INT);
	free_irq(MON_INT, mon_intr_handler);

	cancel_delayed_work_sync(&bw_sample);
	destroy_workqueue(bw_sample_wq);

	bw_levels[0].vectors[0].ib = 0;
	bw_levels[0].vectors[0].ab = 0;
	bw_levels[0].vectors[1].ib = 0;
	bw_levels[0].vectors[1].ab = 0;

	bw_levels[1].vectors[0].ib = 0;
	bw_levels[1].vectors[0].ab = 0;
	bw_levels[1].vectors[1].ib = 0;
	bw_levels[1].vectors[1].ab = 0;
	msm_bus_scale_unregister_client(bus_client);
}
static int start_monitoring(struct devfreq *df)
{
	int ret, mbyte;

	ret = request_threaded_irq(l2pm_irq, NULL, mon_intr_handler,
			  IRQF_ONESHOT | IRQF_SHARED,
			  "cpubw_hwmon", df);
	if (ret) {
		pr_err("Unable to register interrupt handler\n");
		return ret;
	}

	mon_init();
	mon_disable(RD_MON);
	mon_disable(WR_MON);

	mbyte = (df->previous_freq * io_percent) / (2 * 100);
	prev_r_start_val = mon_set_limit_mbyte(RD_MON, mbyte);
	prev_w_start_val = mon_set_limit_mbyte(WR_MON, mbyte);
	prev_ts = ktime_get();
	prev_ab = 0;

	mon_irq_enable(RD_MON, true);
	mon_irq_enable(WR_MON, true);
	mon_enable(RD_MON);
	mon_enable(WR_MON);
	global_mon_enable(true);

	return 0;
}
/* Returns MBps of read/writes for the sampling window. */
static int mon_get_mbps(int n, u32 start_val, unsigned int us)
{
	u32 overflow, count;
	long long beats;

	count = get_l2_indirect_reg(L2PMnEVCNTR(n));
	overflow = get_l2_indirect_reg(L2PMOVSR);

	if (overflow & BIT(n))
		beats = 0xFFFFFFFF - start_val + count;
	else
		beats = count - start_val;

	beats *= USEC_PER_SEC;
	beats *= bytes_per_beat;
	do_div(beats, us);
	beats = DIV_ROUND_UP_ULL(beats, MBYTE);

	pr_debug("EV%d ov: %x, cnt: %x\n", n, overflow, count);

	return beats;
}

static void do_bw_sample(struct work_struct *work);
static DECLARE_DEFERRED_WORK(bw_sample, do_bw_sample);
static struct workqueue_struct *bw_sample_wq;

static DEFINE_MUTEX(bw_lock);
static ktime_t prev_ts;
static u32 prev_r_start_val;
static u32 prev_w_start_val;

static struct msm_bus_paths bw_levels[] = {
	BW(0), BW(200),
};
static struct msm_bus_scale_pdata bw_data = {
	.usecase = bw_levels,
	.num_usecases = ARRAY_SIZE(bw_levels),
	.name = "cpubw-krait",
	.active_only = 1,
};
static u32 bus_client;
static void compute_bw(int mbps);
static irqreturn_t mon_intr_handler(int irq, void *dev_id);

#define START_LIMIT	100 /* MBps */
static int start_monitoring(void)
{
	int mb_limit;
	int ret;

	bw_sample_wq = alloc_workqueue("cpubw-krait", WQ_HIGHPRI, 0);
	if (!bw_sample_wq) {
		pr_err("Unable to alloc workqueue\n");
		return -ENOMEM;
	}

	ret = request_threaded_irq(MON_INT, NULL, mon_intr_handler,
			  IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING,
			  "cpubw_krait", mon_intr_handler);
	if (ret) {
		pr_err("Unable to register interrupt handler\n");
		return ret;
	}

	bus_client = msm_bus_scale_register_client(&bw_data);
	if (!bus_client) {
		pr_err("Unable to register bus client\n");
		ret = -ENODEV;
		goto bus_reg_fail;
	}

	compute_bw(START_LIMIT);

	mon_init();
	mon_disable(0);
	mon_disable(1);

	mb_limit = mult_frac(START_LIMIT, sample_ms, MSEC_PER_SEC);
	mb_limit /= 2;

	prev_r_start_val = mon_set_limit_mbyte(0, mb_limit);
	prev_w_start_val = mon_set_limit_mbyte(1, mb_limit);

	prev_ts = ktime_get();

	set_l2_indirect_reg(L2PMINTENSET, BIT(0));
	set_l2_indirect_reg(L2PMINTENSET, BIT(1));
	mon_enable(0);
	mon_enable(1);
	global_mon_enable(true);

	queue_delayed_work(bw_sample_wq, &bw_sample,
				msecs_to_jiffies(sample_ms));

	return 0;

bus_reg_fail:
	destroy_workqueue(bw_sample_wq);
	disable_irq(MON_INT);
	free_irq(MON_INT, mon_intr_handler);
	return ret;
}
static void stop_monitoring(struct devfreq *df)
{
	global_mon_enable(false);
	mon_disable(RD_MON);
	mon_disable(WR_MON);
	mon_irq_enable(RD_MON, false);
	mon_irq_enable(WR_MON, false);

	disable_irq(l2pm_irq);
	free_irq(l2pm_irq, df);
}