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

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

Merge "msm: lmh_lite: Update to threaded interrupt handling"

parents d851e795 677da23e
Loading
Loading
Loading
Loading
+6 −56
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>
@@ -61,13 +60,11 @@ struct lmh_device_data {
};

struct lmh_mon_sensor_data {
	struct list_head		list_ptr;
	char				sensor_name[LMH_NAME_MAX];
	struct lmh_sensor_ops		*sensor_ops;
	struct delayed_work		isr_poll;
	struct list_head		list_ptr;
	struct rw_semaphore		lock;
	struct lmh_mon_threshold	trip[LMH_TRIP_MAX];
	enum lmh_monitor_state		state;
	struct thermal_zone_device	*tzdev;
	enum thermal_device_mode	mode;
};
@@ -332,44 +329,7 @@ static void lmh_evaluate_and_notify(struct lmh_mon_sensor_data *lmh_sensor,
	}
}

static void lmh_interrupt_monitor(struct work_struct *work)
{
	int ret = 0;
	long val = 0;
	struct lmh_mon_sensor_data *lmh_sensor = container_of(work,
				struct lmh_mon_sensor_data, isr_poll.work);

	down_write(&lmh_sensor->lock);
	ret = lmh_sensor->sensor_ops->read(lmh_sensor->sensor_ops, &val);
	if (ret) {
		pr_err("Error reading the sensor:[%s]. err:%d\n",
			lmh_sensor->sensor_name, ret);
		goto exit_monitor;
	}
	lmh_evaluate_and_notify(lmh_sensor, val);
	if (val <= 0) {
		ret = lmh_sensor->sensor_ops->reset_interrupt(
				lmh_sensor->sensor_ops);
		if (ret == -EAGAIN)
			goto schedule_and_exit;
		else if (ret)
			pr_err("Sensor:[%s] interrupt reset failed. err:%d\n",
					lmh_sensor->sensor_name, ret);
		pr_debug("Rearm sensor:[%s] interrupt\n",
			lmh_sensor->sensor_name);
		lmh_sensor->state = LMH_ISR_MONITOR;
		goto exit_monitor;
	}

schedule_and_exit:
	schedule_delayed_work(&lmh_sensor->isr_poll,
		msecs_to_jiffies(lmh_poll_interval));

exit_monitor:
	up_write(&lmh_sensor->lock);
}

void lmh_interrupt_notify(struct lmh_sensor_ops *ops, long trip_val)
void lmh_update_reading(struct lmh_sensor_ops *ops, long trip_val)
{
	struct lmh_mon_sensor_data *lmh_sensor = NULL;

@@ -385,14 +345,9 @@ void lmh_interrupt_notify(struct lmh_sensor_ops *ops, long trip_val)
		goto interrupt_exit;
	}
	down_write(&lmh_sensor->lock);
	if (lmh_sensor->state != LMH_ISR_MONITOR)
		goto interrupt_exit;
	pr_debug("Sensor:[%s] interrupt triggered with intensity:%ld\n",
			lmh_sensor->sensor_name, trip_val);
	lmh_sensor->state = LMH_ISR_POLLING;
	pr_debug("Sensor:[%s] intensity:%ld\n", lmh_sensor->sensor_name,
		trip_val);
	lmh_evaluate_and_notify(lmh_sensor, trip_val);
	schedule_delayed_work(&lmh_sensor->isr_poll,
		msecs_to_jiffies(lmh_poll_interval));
interrupt_exit:
	if (lmh_sensor)
		up_write(&lmh_sensor->lock);
@@ -557,11 +512,9 @@ static int lmh_sensor_init(struct lmh_mon_sensor_data *lmh_sensor,
{
	int idx = 0, ret = 0;

	lmh_sensor->state = LMH_ISR_MONITOR;
	strlcpy(lmh_sensor->sensor_name, sensor_name, LMH_NAME_MAX);
	lmh_sensor->sensor_ops = ops;
	ops->interrupt_notify = lmh_interrupt_notify;
	INIT_DELAYED_WORK(&lmh_sensor->isr_poll, lmh_interrupt_monitor);
	ops->new_value_notify = lmh_update_reading;
	for (idx = 0; idx < LMH_TRIP_MAX; idx++) {
		lmh_sensor->trip[idx].value = 0;
		lmh_sensor->trip[idx].active = false;
@@ -585,8 +538,7 @@ int lmh_sensor_register(char *sensor_name, struct lmh_sensor_ops *ops)
		return -EINVAL;
	}

	if (!ops->read || !ops->reset_interrupt || !ops->enable_hw_log
		|| !ops->disable_hw_log) {
	if (!ops->read || !ops->enable_hw_log || !ops->disable_hw_log) {
		pr_err("Invalid ops input for sensor:%s\n", sensor_name);
		return -EINVAL;
	}
@@ -637,8 +589,6 @@ static void lmh_sensor_remove(struct lmh_sensor_ops *ops)
		goto deregister_exit;
	}
	down_write(&lmh_sensor->lock);
	cancel_delayed_work_sync(&lmh_sensor->isr_poll);
	lmh_sensor->state = LMH_ISR_DISABLED;
	thermal_zone_device_unregister(lmh_sensor->tzdev);
	list_del(&lmh_sensor->list_ptr);
	up_write(&lmh_sensor->lock);
+7 −8
Original line number Diff line number Diff line
@@ -33,10 +33,9 @@ enum lmh_monitor_state {

struct lmh_sensor_ops {
	int (*read)(struct lmh_sensor_ops *, long *);
	int (*reset_interrupt) (struct lmh_sensor_ops *);
	int (*enable_hw_log)(uint32_t, uint32_t);
	int (*disable_hw_log)(void);
	void (*interrupt_notify) (struct lmh_sensor_ops *, long);
	void (*new_value_notify)(struct lmh_sensor_ops *, long);
};

struct lmh_device_ops {
+54 −118
Original line number Diff line number Diff line
@@ -140,8 +140,6 @@ struct lmh_debug {

struct lmh_driver_data {
	struct device			*dev;
	struct workqueue_struct		*isr_wq;
	struct work_struct		isr_work;
	struct delayed_work		poll_work;
	uint32_t			log_enabled;
	uint32_t			log_delay;
@@ -166,7 +164,6 @@ struct lmh_sensor_data {
	uint32_t			sensor_hw_node_id;
	int				sensor_sw_id;
	struct lmh_sensor_ops		ops;
	enum lmh_monitor_state		state;
	long				last_read_value;
	struct list_head		list_ptr;
};
@@ -264,64 +261,27 @@ enable_exit:
	return ret;
}

static int lmh_reset(struct lmh_sensor_ops *ops)
static void lmh_update(struct lmh_driver_data *lmh_dat,
	struct lmh_sensor_data *lmh_sensor)
{
	int ret = 0;
	struct lmh_sensor_data *lmh_iter_sensor = NULL;
	struct lmh_sensor_data *lmh_sensor = container_of(ops,
		       struct lmh_sensor_data, ops);

	down_write(&lmh_sensor_access);
	if (lmh_data->intr_status_val & BIT(lmh_sensor->sensor_sw_id)) {
		if (lmh_sensor->last_read_value) {
			ret = -EAGAIN;
			goto reset_exit;
		}
		lmh_data->intr_status_val ^= BIT(lmh_sensor->sensor_sw_id);
		lmh_sensor->state = LMH_ISR_MONITOR;
		pr_debug("Sensor:[%s] not throttling. Switch to monitor mode\n",
	if (lmh_sensor->last_read_value > 0 && !(lmh_dat->intr_status_val
		& BIT(lmh_sensor->sensor_sw_id))) {
		pr_debug("Sensor:[%s] interrupt triggered\n",
			lmh_sensor->sensor_name);
		trace_lmh_sensor_interrupt(lmh_sensor->sensor_name,
			lmh_sensor->last_read_value);
	} else {
		pr_err("Sensor:[%s] is already in reset state\n",
		lmh_dat->intr_status_val |= BIT(lmh_sensor->sensor_sw_id);
	} else if (lmh_sensor->last_read_value == 0 && (lmh_dat->intr_status_val
		& BIT(lmh_sensor->sensor_sw_id))) {
		pr_debug("Sensor:[%s] interrupt clear\n",
			lmh_sensor->sensor_name);
	}
		trace_lmh_sensor_interrupt(lmh_sensor->sensor_name,
			lmh_sensor->last_read_value);

	if (!lmh_data->intr_status_val) {
		/*
		 * Scan through the sensor list and abort the interrupt
		 * enable if any of the sensor is still throttling
		 */
		list_for_each_entry(lmh_iter_sensor, &lmh_sensor_list,
			list_ptr) {
			if (lmh_iter_sensor->last_read_value) {
				pr_debug("Sensor:[%s] retrigger interrupt\n",
					lmh_iter_sensor->sensor_name);
				lmh_data->intr_status_val
					|= BIT(lmh_iter_sensor->sensor_sw_id);
				lmh_iter_sensor->state = LMH_ISR_POLLING;
				lmh_iter_sensor->ops.interrupt_notify(
					&lmh_iter_sensor->ops,
					lmh_iter_sensor->last_read_value);
			}
		}
		if (!lmh_data->intr_status_val) {
			lmh_data->intr_state = LMH_ISR_MONITOR;
			pr_debug("Zero throttling. Re-enabling interrupt\n");
			/*
			 * Don't use cancel_delayed_work_sync as it will lead
			 * to deadlock because of the mutex
			 */
			cancel_delayed_work(&lmh_data->poll_work);
			trace_lmh_event_call("Lmh Interrupt Clear");
			enable_irq(lmh_data->irq_num);
		}
		lmh_data->intr_status_val ^= BIT(lmh_sensor->sensor_sw_id);
	}

reset_exit:
	up_write(&lmh_sensor_access);
	return ret;
	lmh_sensor->ops.new_value_notify(&lmh_sensor->ops,
		lmh_sensor->last_read_value);
}

static void lmh_read_and_update(struct lmh_driver_data *lmh_dat)
@@ -336,7 +296,6 @@ static void lmh_read_and_update(struct lmh_driver_data *lmh_dat)
		uint32_t size;
	} cmd_buf;


	mutex_lock(&lmh_sensor_read);
	list_for_each_entry(lmh_sensor, &lmh_sensor_list, list_ptr)
		lmh_sensor->last_read_value = 0;
@@ -387,30 +346,10 @@ static void lmh_read_and_update(struct lmh_driver_data *lmh_dat)

read_exit:
	mutex_unlock(&lmh_sensor_read);
	return;
}

static void lmh_read_and_notify(struct lmh_driver_data *lmh_dat)
{
	struct lmh_sensor_data *lmh_sensor = NULL;
	long val;

	lmh_read_and_update(lmh_dat);
	list_for_each_entry(lmh_sensor, &lmh_sensor_list, list_ptr) {
		val = lmh_sensor->last_read_value;
		if (val > 0 && !(lmh_dat->intr_status_val
			& BIT(lmh_sensor->sensor_sw_id))) {
			pr_debug("Sensor:[%s] interrupt triggered\n",
				lmh_sensor->sensor_name);
			trace_lmh_sensor_interrupt(lmh_sensor->sensor_name,
							val);
			lmh_dat->intr_status_val
			       |= BIT(lmh_sensor->sensor_sw_id);
			lmh_sensor->state = LMH_ISR_POLLING;
			lmh_sensor->ops.interrupt_notify(&lmh_sensor->ops, val);
		}
	}
	list_for_each_entry(lmh_sensor, &lmh_sensor_list, list_ptr)
		lmh_update(lmh_dat, lmh_sensor);

	return;
}

static void lmh_poll(struct work_struct *work)
@@ -421,9 +360,17 @@ static void lmh_poll(struct work_struct *work)
	down_write(&lmh_sensor_access);
	if (lmh_dat->intr_state != LMH_ISR_POLLING)
		goto poll_exit;
	lmh_read_and_notify(lmh_dat);
	lmh_read_and_update(lmh_dat);
	if (!lmh_data->intr_status_val) {
		lmh_data->intr_state = LMH_ISR_MONITOR;
		pr_debug("Zero throttling. Re-enabling interrupt\n");
		trace_lmh_event_call("Lmh Interrupt Clear");
		enable_irq(lmh_data->irq_num);
		goto poll_exit;
	} else {
		schedule_delayed_work(&lmh_dat->poll_work,
			msecs_to_jiffies(lmh_poll_interval));
	}

poll_exit:
	up_write(&lmh_sensor_access);
@@ -452,14 +399,26 @@ static void lmh_trim_error(void)
	return;
}

static void lmh_notify(struct work_struct *work)
static irqreturn_t lmh_handle_isr(int irq, void *dev_id)
{
	struct lmh_driver_data *lmh_dat = container_of(work,
			struct lmh_driver_data, isr_work);
	disable_irq_nosync(irq);
	return IRQ_WAKE_THREAD;
}

static irqreturn_t lmh_isr_thread(int irq, void *data)
{
	struct lmh_driver_data *lmh_dat = data;

	pr_debug("LMH Interrupt triggered\n");
	trace_lmh_event_call("Lmh Interrupt");

	/* Cancel any pending polling work event before scheduling new one */
	cancel_delayed_work_sync(&lmh_dat->poll_work);
	down_write(&lmh_sensor_access);
	if (lmh_dat->intr_state != LMH_ISR_MONITOR) {
		pr_err("Invalid software state\n");
		trace_lmh_event_call("Invalid software state");
		WARN_ON(1);
		goto isr_unlock_exit;
	}
	lmh_dat->intr_state = LMH_ISR_POLLING;
	if (!lmh_data->trim_err_disable) {
		lmh_dat->intr_reg_val = readl_relaxed(lmh_dat->intr_addr);
@@ -468,38 +427,26 @@ static void lmh_notify(struct work_struct *work)
			trace_lmh_event_call("Lmh trim error");
			lmh_trim_error();
			lmh_dat->intr_state = LMH_ISR_MONITOR;
			goto notify_exit;
			goto decide_next_action;
		}
	}
	lmh_read_and_notify(lmh_dat);
	lmh_read_and_update(lmh_dat);
	if (!lmh_dat->intr_status_val) {
		pr_debug("LMH not throttling. Enabling interrupt\n");
		lmh_dat->intr_state = LMH_ISR_MONITOR;
		trace_lmh_event_call("Lmh Zero throttle Interrupt Clear");
		goto notify_exit;
		goto decide_next_action;
	}

notify_exit:
decide_next_action:
	if (lmh_dat->intr_state == LMH_ISR_POLLING)
		schedule_delayed_work(&lmh_dat->poll_work,
			msecs_to_jiffies(lmh_poll_interval));
	else
		enable_irq(lmh_dat->irq_num);
	up_write(&lmh_sensor_access);
	return;
}

static irqreturn_t lmh_handle_isr(int irq, void *data)
{
	struct lmh_driver_data *lmh_dat = data;

	pr_debug("LMH Interrupt triggered\n");
	trace_lmh_event_call("Lmh Interrupt");
	if (lmh_dat->intr_state == LMH_ISR_MONITOR) {
		disable_irq_nosync(lmh_dat->irq_num);
		queue_work(lmh_dat->isr_wq, &lmh_dat->isr_work);
	}

isr_unlock_exit:
	up_write(&lmh_sensor_access);
	return IRQ_HANDLED;
}

@@ -538,8 +485,9 @@ static int lmh_get_sensor_devicetree(struct platform_device *pdev)
		goto dev_exit;
	}

	ret = request_irq(lmh_data->irq_num, lmh_handle_isr,
				IRQF_TRIGGER_HIGH, LMH_INTERRUPT, lmh_data);
	ret = request_threaded_irq(lmh_data->irq_num, lmh_handle_isr,
		lmh_isr_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
		LMH_INTERRUPT, lmh_data);
	if (ret) {
		pr_err("Error getting irq for LMH. err:%d\n", ret);
		goto dev_exit;
@@ -641,8 +589,6 @@ static int lmh_parse_sensor(struct lmh_sensor_info *sens_info)
	lmh_sensor->ops.read = lmh_read;
	lmh_sensor->ops.disable_hw_log = lmh_disable_log;
	lmh_sensor->ops.enable_hw_log = lmh_enable_log;
	lmh_sensor->ops.reset_interrupt = lmh_reset;
	lmh_sensor->state = LMH_ISR_MONITOR;
	lmh_sensor->sensor_sw_id = lmh_data->max_sensor_count++;
	lmh_sensor->sensor_hw_name = sens_info->name;
	lmh_sensor->sensor_hw_node_id = sens_info->node_id;
@@ -1251,13 +1197,6 @@ static int lmh_probe(struct platform_device *pdev)
	}
	lmh_data->dev = &pdev->dev;

	lmh_data->isr_wq = alloc_workqueue("lmh_isr_wq", WQ_HIGHPRI, 0);
	if (!lmh_data->isr_wq) {
		pr_err("Error allocating workqueue\n");
		ret = -ENOMEM;
		goto probe_exit;
	}
	INIT_WORK(&lmh_data->isr_work, lmh_notify);
	INIT_DEFERRABLE_WORK(&lmh_data->poll_work, lmh_poll);
	ret = lmh_sensor_init(pdev);
	if (ret) {
@@ -1284,8 +1223,6 @@ static int lmh_probe(struct platform_device *pdev)
	return ret;

probe_exit:
	if (lmh_data->isr_wq)
		destroy_workqueue(lmh_data->isr_wq);
	lmh_data = NULL;
	return ret;
}
@@ -1295,7 +1232,6 @@ static int lmh_remove(struct platform_device *pdev)
	struct lmh_driver_data *lmh_dat = platform_get_drvdata(pdev);

	cancel_delayed_work_sync(&lmh_dat->poll_work);
	destroy_workqueue(lmh_dat->isr_wq);
	free_irq(lmh_dat->irq_num, lmh_dat);
	lmh_remove_sensors();
	lmh_device_deregister(&lmh_dat->dev_info.dev_ops);