162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011 Jonathan Cameron
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Event handling elements of industrial I/O reference driver.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/irq.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/iio/iio.h>
1362306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1462306a36Sopenharmony_ci#include <linux/iio/events.h>
1562306a36Sopenharmony_ci#include "iio_simple_dummy.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Evgen 'fakes' interrupt events for this example */
1862306a36Sopenharmony_ci#include "iio_dummy_evgen.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/**
2162306a36Sopenharmony_ci * iio_simple_dummy_read_event_config() - is event enabled?
2262306a36Sopenharmony_ci * @indio_dev: the device instance data
2362306a36Sopenharmony_ci * @chan: channel for the event whose state is being queried
2462306a36Sopenharmony_ci * @type: type of the event whose state is being queried
2562306a36Sopenharmony_ci * @dir: direction of the vent whose state is being queried
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * This function would normally query the relevant registers or a cache to
2862306a36Sopenharmony_ci * discover if the event generation is enabled on the device.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ciint iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
3162306a36Sopenharmony_ci				       const struct iio_chan_spec *chan,
3262306a36Sopenharmony_ci				       enum iio_event_type type,
3362306a36Sopenharmony_ci				       enum iio_event_direction dir)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return st->event_en;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * iio_simple_dummy_write_event_config() - set whether event is enabled
4262306a36Sopenharmony_ci * @indio_dev: the device instance data
4362306a36Sopenharmony_ci * @chan: channel for the event whose state is being set
4462306a36Sopenharmony_ci * @type: type of the event whose state is being set
4562306a36Sopenharmony_ci * @dir: direction of the vent whose state is being set
4662306a36Sopenharmony_ci * @state: whether to enable or disable the device.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * This function would normally set the relevant registers on the devices
4962306a36Sopenharmony_ci * so that it generates the specified event. Here it just sets up a cached
5062306a36Sopenharmony_ci * value.
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ciint iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
5362306a36Sopenharmony_ci					const struct iio_chan_spec *chan,
5462306a36Sopenharmony_ci					enum iio_event_type type,
5562306a36Sopenharmony_ci					enum iio_event_direction dir,
5662306a36Sopenharmony_ci					int state)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 *  Deliberately over the top code splitting to illustrate
6262306a36Sopenharmony_ci	 * how this is done when multiple events exist.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	switch (chan->type) {
6562306a36Sopenharmony_ci	case IIO_VOLTAGE:
6662306a36Sopenharmony_ci		switch (type) {
6762306a36Sopenharmony_ci		case IIO_EV_TYPE_THRESH:
6862306a36Sopenharmony_ci			if (dir == IIO_EV_DIR_RISING)
6962306a36Sopenharmony_ci				st->event_en = state;
7062306a36Sopenharmony_ci			else
7162306a36Sopenharmony_ci				return -EINVAL;
7262306a36Sopenharmony_ci			break;
7362306a36Sopenharmony_ci		default:
7462306a36Sopenharmony_ci			return -EINVAL;
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci		break;
7762306a36Sopenharmony_ci	case IIO_ACTIVITY:
7862306a36Sopenharmony_ci		switch (type) {
7962306a36Sopenharmony_ci		case IIO_EV_TYPE_THRESH:
8062306a36Sopenharmony_ci			st->event_en = state;
8162306a36Sopenharmony_ci			break;
8262306a36Sopenharmony_ci		default:
8362306a36Sopenharmony_ci			return -EINVAL;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci		break;
8662306a36Sopenharmony_ci	case IIO_STEPS:
8762306a36Sopenharmony_ci		switch (type) {
8862306a36Sopenharmony_ci		case IIO_EV_TYPE_CHANGE:
8962306a36Sopenharmony_ci			st->event_en = state;
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci		default:
9262306a36Sopenharmony_ci			return -EINVAL;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	default:
9662306a36Sopenharmony_ci		return -EINVAL;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * iio_simple_dummy_read_event_value() - get value associated with event
10462306a36Sopenharmony_ci * @indio_dev: device instance specific data
10562306a36Sopenharmony_ci * @chan: channel for the event whose value is being read
10662306a36Sopenharmony_ci * @type: type of the event whose value is being read
10762306a36Sopenharmony_ci * @dir: direction of the vent whose value is being read
10862306a36Sopenharmony_ci * @info: info type of the event whose value is being read
10962306a36Sopenharmony_ci * @val: value for the event code.
11062306a36Sopenharmony_ci * @val2: unused
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * Many devices provide a large set of events of which only a subset may
11362306a36Sopenharmony_ci * be enabled at a time, with value registers whose meaning changes depending
11462306a36Sopenharmony_ci * on the event enabled. This often means that the driver must cache the values
11562306a36Sopenharmony_ci * associated with each possible events so that the right value is in place when
11662306a36Sopenharmony_ci * the enabled event is changed.
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_ciint iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
11962306a36Sopenharmony_ci				      const struct iio_chan_spec *chan,
12062306a36Sopenharmony_ci				      enum iio_event_type type,
12162306a36Sopenharmony_ci				      enum iio_event_direction dir,
12262306a36Sopenharmony_ci				      enum iio_event_info info,
12362306a36Sopenharmony_ci				      int *val, int *val2)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	*val = st->event_val;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return IIO_VAL_INT;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * iio_simple_dummy_write_event_value() - set value associate with event
13462306a36Sopenharmony_ci * @indio_dev: device instance specific data
13562306a36Sopenharmony_ci * @chan: channel for the event whose value is being set
13662306a36Sopenharmony_ci * @type: type of the event whose value is being set
13762306a36Sopenharmony_ci * @dir: direction of the vent whose value is being set
13862306a36Sopenharmony_ci * @info: info type of the event whose value is being set
13962306a36Sopenharmony_ci * @val: the value to be set.
14062306a36Sopenharmony_ci * @val2: unused
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ciint iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
14362306a36Sopenharmony_ci				       const struct iio_chan_spec *chan,
14462306a36Sopenharmony_ci				       enum iio_event_type type,
14562306a36Sopenharmony_ci				       enum iio_event_direction dir,
14662306a36Sopenharmony_ci				       enum iio_event_info info,
14762306a36Sopenharmony_ci				       int val, int val2)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	st->event_val = val;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct iio_dev *indio_dev = private;
15962306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	st->event_timestamp = iio_get_time_ns(indio_dev);
16262306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/**
16662306a36Sopenharmony_ci * iio_simple_dummy_event_handler() - identify and pass on event
16762306a36Sopenharmony_ci * @irq: irq of event line
16862306a36Sopenharmony_ci * @private: pointer to device instance state.
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * This handler is responsible for querying the device to find out what
17162306a36Sopenharmony_ci * event occurred and for then pushing that event towards userspace.
17262306a36Sopenharmony_ci * Here only one event occurs so we push that directly on with locally
17362306a36Sopenharmony_ci * grabbed timestamp.
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistatic irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct iio_dev *indio_dev = private;
17862306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	dev_dbg(&indio_dev->dev, "id %x event %x\n",
18162306a36Sopenharmony_ci		st->regs->reg_id, st->regs->reg_data);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	switch (st->regs->reg_data) {
18462306a36Sopenharmony_ci	case 0:
18562306a36Sopenharmony_ci		iio_push_event(indio_dev,
18662306a36Sopenharmony_ci			       IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
18762306a36Sopenharmony_ci					      IIO_EV_DIR_RISING,
18862306a36Sopenharmony_ci					      IIO_EV_TYPE_THRESH, 0, 0, 0),
18962306a36Sopenharmony_ci			       st->event_timestamp);
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case 1:
19262306a36Sopenharmony_ci		if (st->activity_running > st->event_val)
19362306a36Sopenharmony_ci			iio_push_event(indio_dev,
19462306a36Sopenharmony_ci				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
19562306a36Sopenharmony_ci						      IIO_MOD_RUNNING,
19662306a36Sopenharmony_ci						      IIO_EV_DIR_RISING,
19762306a36Sopenharmony_ci						      IIO_EV_TYPE_THRESH,
19862306a36Sopenharmony_ci						      0, 0, 0),
19962306a36Sopenharmony_ci				       st->event_timestamp);
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	case 2:
20262306a36Sopenharmony_ci		if (st->activity_walking < st->event_val)
20362306a36Sopenharmony_ci			iio_push_event(indio_dev,
20462306a36Sopenharmony_ci				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
20562306a36Sopenharmony_ci						      IIO_MOD_WALKING,
20662306a36Sopenharmony_ci						      IIO_EV_DIR_FALLING,
20762306a36Sopenharmony_ci						      IIO_EV_TYPE_THRESH,
20862306a36Sopenharmony_ci						      0, 0, 0),
20962306a36Sopenharmony_ci				       st->event_timestamp);
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case 3:
21262306a36Sopenharmony_ci		iio_push_event(indio_dev,
21362306a36Sopenharmony_ci			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
21462306a36Sopenharmony_ci					      IIO_EV_DIR_NONE,
21562306a36Sopenharmony_ci					      IIO_EV_TYPE_CHANGE, 0, 0, 0),
21662306a36Sopenharmony_ci			       st->event_timestamp);
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci	default:
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return IRQ_HANDLED;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/**
22662306a36Sopenharmony_ci * iio_simple_dummy_events_register() - setup interrupt handling for events
22762306a36Sopenharmony_ci * @indio_dev: device instance data
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * This function requests the threaded interrupt to handle the events.
23062306a36Sopenharmony_ci * Normally the irq is a hardware interrupt and the number comes
23162306a36Sopenharmony_ci * from board configuration files.  Here we get it from a companion
23262306a36Sopenharmony_ci * module that fakes the interrupt for us. Note that module in
23362306a36Sopenharmony_ci * no way forms part of this example. Just assume that events magically
23462306a36Sopenharmony_ci * appear via the provided interrupt.
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_ciint iio_simple_dummy_events_register(struct iio_dev *indio_dev)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
23962306a36Sopenharmony_ci	int ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Fire up event source - normally not present */
24262306a36Sopenharmony_ci	st->event_irq = iio_dummy_evgen_get_irq();
24362306a36Sopenharmony_ci	if (st->event_irq < 0) {
24462306a36Sopenharmony_ci		ret = st->event_irq;
24562306a36Sopenharmony_ci		goto error_ret;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	st->regs = iio_dummy_evgen_get_regs(st->event_irq);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ret = request_threaded_irq(st->event_irq,
25062306a36Sopenharmony_ci				   &iio_simple_dummy_get_timestamp,
25162306a36Sopenharmony_ci				   &iio_simple_dummy_event_handler,
25262306a36Sopenharmony_ci				   IRQF_ONESHOT,
25362306a36Sopenharmony_ci				   "iio_simple_event",
25462306a36Sopenharmony_ci				   indio_dev);
25562306a36Sopenharmony_ci	if (ret < 0)
25662306a36Sopenharmony_ci		goto error_free_evgen;
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cierror_free_evgen:
26062306a36Sopenharmony_ci	iio_dummy_evgen_release_irq(st->event_irq);
26162306a36Sopenharmony_cierror_ret:
26262306a36Sopenharmony_ci	return ret;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove
26762306a36Sopenharmony_ci * @indio_dev: device instance data
26862306a36Sopenharmony_ci */
26962306a36Sopenharmony_civoid iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct iio_dummy_state *st = iio_priv(indio_dev);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	free_irq(st->event_irq, indio_dev);
27462306a36Sopenharmony_ci	/* Not part of normal driver */
27562306a36Sopenharmony_ci	iio_dummy_evgen_release_irq(st->event_irq);
27662306a36Sopenharmony_ci}
277