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

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

Merge "drivers: thermal: limits-dcvs: Prevent race in freq limits polling"

parents a6acbcf4 47f9b50d
Loading
Loading
Loading
Loading
+21 −17
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * 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
@@ -98,13 +98,13 @@ struct limits_dcvs_hw {
	void *int_clr_reg;
	void *min_freq_reg;
	cpumask_t core_map;
	struct timer_list poll_timer;
	struct delayed_work freq_poll_work;
	unsigned long max_freq;
	unsigned long min_freq;
	unsigned long hw_freq_limit;
	struct device_attribute lmh_freq_attr;
	struct list_head list;
	atomic_t is_irq_enabled;
	bool is_irq_enabled;
	struct mutex access_lock;
	struct __limits_cdev_data *cdev_data;
	struct regulator *isens_reg;
@@ -184,33 +184,37 @@ static unsigned long limits_mitigation_notify(struct limits_dcvs_hw *hw)
	return max_limit;
}

static void limits_dcvs_poll(unsigned long data)
static void limits_dcvs_poll(struct work_struct *work)
{
	unsigned long max_limit = 0;
	struct limits_dcvs_hw *hw = (struct limits_dcvs_hw *)data;
	struct limits_dcvs_hw *hw = container_of(work,
					struct limits_dcvs_hw,
					freq_poll_work.work);

	mutex_lock(&hw->access_lock);
	if (hw->max_freq == UINT_MAX)
		limits_dcvs_get_freq_limits(cpumask_first(&hw->core_map),
			&hw->max_freq, &hw->min_freq);
	max_limit = limits_mitigation_notify(hw);
	if (max_limit >= hw->max_freq) {
		del_timer(&hw->poll_timer);
		writel_relaxed(0xFF, hw->int_clr_reg);
		atomic_set(&hw->is_irq_enabled, 1);
		hw->is_irq_enabled = true;
		enable_irq(hw->irq_num);
	} else {
		mod_timer(&hw->poll_timer, jiffies + msecs_to_jiffies(
			LIMITS_POLLING_DELAY_MS));
		mod_delayed_work(system_highpri_wq, &hw->freq_poll_work,
			 msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
	}
	mutex_unlock(&hw->access_lock);
}

static void lmh_dcvs_notify(struct limits_dcvs_hw *hw)
{
	if (atomic_dec_and_test(&hw->is_irq_enabled)) {
	if (hw->is_irq_enabled) {
		hw->is_irq_enabled = false;
		disable_irq_nosync(hw->irq_num);
		limits_mitigation_notify(hw);
		mod_timer(&hw->poll_timer, jiffies + msecs_to_jiffies(
			LIMITS_POLLING_DELAY_MS));
		mod_delayed_work(system_highpri_wq, &hw->freq_poll_work,
			 msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
	}
}

@@ -218,7 +222,9 @@ static irqreturn_t lmh_dcvs_handle_isr(int irq, void *data)
{
	struct limits_dcvs_hw *hw = data;

	mutex_lock(&hw->access_lock);
	lmh_dcvs_notify(hw);
	mutex_unlock(&hw->access_lock);

	return IRQ_HANDLED;
}
@@ -373,8 +379,8 @@ static int lmh_set_max_limit(int cpu, u32 freq)
	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
				  LIMITS_FREQ_CAP, max_freq,
				  (max_freq == U32_MAX) ? 0 : 1, 1);
	mutex_unlock(&hw->access_lock);
	lmh_dcvs_notify(hw);
	mutex_unlock(&hw->access_lock);

	return ret;
}
@@ -626,9 +632,7 @@ static int limits_dcvs_probe(struct platform_device *pdev)
	}

	mutex_init(&hw->access_lock);
	init_timer_deferrable(&hw->poll_timer);
	hw->poll_timer.data = (unsigned long)hw;
	hw->poll_timer.function = limits_dcvs_poll;
	INIT_DEFERRABLE_WORK(&hw->freq_poll_work, limits_dcvs_poll);
	hw->osm_hw_reg = devm_ioremap(&pdev->dev, request_reg, 0x4);
	if (!hw->osm_hw_reg) {
		pr_err("register remap failed\n");
@@ -645,7 +649,7 @@ static int limits_dcvs_probe(struct platform_device *pdev)
		pr_err("Error getting IRQ number. err:%d\n", hw->irq_num);
		goto probe_exit;
	}
	atomic_set(&hw->is_irq_enabled, 1);
	hw->is_irq_enabled = true;
	ret = devm_request_threaded_irq(&pdev->dev, hw->irq_num, NULL,
		lmh_dcvs_handle_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT
		| IRQF_NO_SUSPEND, hw->sensor_name, hw);