#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
+#include <linux/iio/events.h>
#include <asm/unaligned.h>
+#include <linux/interrupt.h>
#include <linux/iio/common/st_sensors.h>
}
EXPORT_SYMBOL(st_sensors_sysfs_scale_avail);
+static struct st_sensor_event *st_sensor_find_event_data(struct
+ st_sensor_data * sdata, u64 event_code)
+{
+ int i;
+ int mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code);
+ int type = IIO_EVENT_CODE_EXTRACT_TYPE(event_code);
+ int chan_type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code);
+ int dir = IIO_EVENT_CODE_EXTRACT_DIR(event_code);
+ struct st_sensor_event_irq *irq = &sdata->sensor->event_irq;
+ struct st_sensor_event *event;
+
+ if (irq->event_count == 0)
+ return NULL;
+
+ for (i = 0; i < irq->event_count; i++) {
+ event = &irq->events[i];
+
+ if (event->modifier == mod &&
+ event->event_type == type &&
+ event->direction == dir &&
+ event->chan_type == chan_type)
+ return event;
+ }
+
+ return NULL;
+}
+
+int st_sensors_read_event_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event *event =
+ st_sensor_find_event_data(sdata, event_code);
+
+ if (event)
+ return (bool)(sdata->events_flag & (1 << event->bit));
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(st_sensors_read_event_config);
+
+int st_sensors_write_event_config(struct iio_dev *indio_dev,
+ u64 event_code,
+ int state)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event *event =
+ st_sensor_find_event_data(sdata, event_code);
+ int unsigned mask;
+ int err = -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (!event)
+ goto error;
+
+ mask = (1 << event->bit);
+ err = st_sensors_write_data_with_mask(indio_dev,
+ sdata->sensor->event_irq.ctrl_reg.addr,
+ mask, (bool)state);
+ if (err < 0)
+ goto error;
+
+ if (state)
+ sdata->events_flag |= mask;
+ else
+ sdata->events_flag &= ~mask;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+
+error:
+ mutex_unlock(&indio_dev->mlock);
+ return err;
+}
+EXPORT_SYMBOL(st_sensors_write_event_config);
+
+int st_sensors_read_event_value(struct iio_dev *indio_dev,
+ u64 event_code,
+ int *val)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event *event =
+ st_sensor_find_event_data(sdata, event_code);
+ u8 byte;
+ int err;
+
+ if (!event)
+ goto error;
+
+ mutex_lock(&indio_dev->mlock);
+
+ err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
+ event->event_ths_reg.addr, &byte);
+ if (!err)
+ *val = byte;
+
+ mutex_unlock(&indio_dev->mlock);
+ return err;
+
+error:
+ return -EINVAL;
+
+}
+EXPORT_SYMBOL(st_sensors_read_event_value);
+
+int st_sensors_write_event_value(struct iio_dev *indio_dev,
+ u64 event_code,
+ int val)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event *event =
+ st_sensor_find_event_data(sdata, event_code);
+ int err;
+
+ if (!event)
+ goto error;
+
+ mutex_lock(&indio_dev->mlock);
+
+ err = st_sensors_write_data_with_mask(indio_dev,
+ event->event_ths_reg.addr,
+ event->event_ths_reg.mask,
+ (u8)val);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return err;
+error:
+ return -EINVAL;
+
+}
+EXPORT_SYMBOL(st_sensors_write_event_value);
+
+static irqreturn_t st_sensor_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event_irq *irq_data =
+ &sdata->sensor->event_irq;
+ struct st_sensor_event *event;
+ s64 timestamp = iio_get_time_ns();
+ u8 status, mask, i;
+ int err = -EIO;
+
+ if (sdata)
+ err = sdata->tf->read_byte(&sdata->tb,
+ sdata->dev,
+ irq_data->status_reg.addr,
+ &status);
+
+ if (err < 0)
+ goto exit;
+
+ for (i = 0; i < irq_data->event_count; i++) {
+ event = &irq_data->events[i];
+ mask = (1 << event->bit);
+ if (status & mask)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(event->chan_type,
+ 0,
+ event->modifier,
+ event->event_type,
+ event->direction),
+ timestamp);
+ }
+
+exit:
+
+ return IRQ_HANDLED;
+}
+
+int st_sensors_request_event_irq(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+ err = request_threaded_irq(sdata->get_irq_event(indio_dev),
+ NULL,
+ st_sensor_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&indio_dev->dev),
+ indio_dev);
+
+ return err;
+}
+
+int st_sensors_enable_events(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ struct st_sensor_event_irq *irq = &sdata->sensor->event_irq;
+
+ err = st_sensors_write_data_with_mask(indio_dev,
+ irq->addr,
+ irq->mask,
+ 1);
+
+ if (err < 0)
+ goto error;
+
+ err = st_sensors_write_data_with_mask(indio_dev,
+ irq->ctrl_reg.addr,
+ irq->ctrl_reg.mask,
+ irq->ctrl_reg.val);
+error:
+ return err;
+}
+EXPORT_SYMBOL(st_sensors_enable_events);
+
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
MODULE_LICENSE("GPL v2");
return sdata->irq_map[ST_SENSORS_INT1];
}
+static unsigned int st_sensors_i2c_get_event_irq(struct iio_dev *indio_dev)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+ return sdata->irq_map[ST_SENSORS_INT2];
+}
+
static void st_sensors_i2c_map_irqs(struct i2c_client *client,
struct iio_dev *indio_dev)
{
st_sensors_i2c_map_irqs(client, indio_dev);
sdata->get_irq_data_ready = st_sensors_i2c_get_data_rdy_irq;
+ sdata->get_irq_event = st_sensors_i2c_get_event_irq;
}
EXPORT_SYMBOL(st_sensors_i2c_configure);
return sdata->irq_map[ST_SENSORS_INT1];
}
+static unsigned int st_sensors_spi_get_event_irq(struct iio_dev *indio_dev)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+ return sdata->irq_map[ST_SENSORS_INT2];
+}
+
static void st_sensors_spi_map_irqs(struct spi_device *spi,
struct iio_dev *indio_dev)
{
st_sensors_spi_map_irqs(spi, indio_dev);
sdata->get_irq_data_ready = st_sensors_spi_get_data_rdy_irq;
+ sdata->get_irq_event = st_sensors_spi_get_event_irq;
}
EXPORT_SYMBOL(st_sensors_spi_configure);
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/irqreturn.h>
+#include <linux/iio/events.h>
#include <linux/iio/trigger.h>
#include <linux/bitops.h>
#define ST_SENSORS_ODR_LIST_MAX 10
#define ST_SENSORS_FULLSCALE_AVL_MAX 10
+#define ST_SENSORS_EVENTS_MAX 10
#define ST_SENSORS_NUMBER_ALL_CHANNELS 4
#define ST_SENSORS_NUMBER_DATA_CHANNELS 3
#define ST_SENSORS_MAP_ONLY_DRDY_IRQ 1
#define ST_SENSORS_MAP_ONLY_EVENT_IRQ 2
+#define ST_SENSORS_LSM_EVENTS_MASK \
+ (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
+ IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
{ \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = index, \
+ .channel = mod, \
.channel2 = mod, \
.address = addr, \
.scan_type = { \
.storagebits = 16, \
.endianness = endian, \
}, \
+ .event_mask = ST_SENSORS_LSM_EVENTS_MASK, \
}
#define ST_SENSOR_DEV_ATTR_SAMP_FREQ() \
struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
};
+struct st_sensor_register {
+ u8 addr;
+ u8 mask;
+ u8 val;
+};
+
/**
* struct st_sensor_bdu - ST sensor device block data update
* @addr: address of the register.
};
/**
+ * struct st_sensor_event - event data.
+ * @bit: position of bit in status register related to event
+ * @chan: channel number.
+ * @chan_type: channel type.
+ * @modifier: modifier for the channel.
+ * @event_type: type of the event.
+ * @direction: direction of the event.
+ * @event_ths_reg: represents the threshold
+ * register of event.
+ */
+struct st_sensor_event {
+ u8 bit;
+ u8 chan;
+ enum iio_chan_type chan_type;
+ enum iio_modifier modifier;
+ enum iio_event_type event_type;
+ enum iio_event_direction direction;
+ struct st_sensor_register event_ths_reg;
+};
+
+/**
+ * struct st_sensor_event_irq -ST sensor event interrupt.
+ * @addr: address of the interrupt register.
+ * @mask: mask to write on/off value.
+ * @event_count: number of events declared in @events array.
+ * @ctrl_reg: represents the control register
+ * of event system
+ * @status_reg: status register of event subsystem.
+ * @events array: of driver events declared by user
+ */
+struct st_sensor_event_irq {
+ u8 addr;
+ u8 mask;
+ u8 event_count;
+ struct st_sensor_register ctrl_reg;
+ struct st_sensor_register status_reg;
+ struct st_sensor_event events[ST_SENSORS_EVENTS_MAX];
+};
+
+
+/**
* struct st_sensor_transfer_buffer - ST sensor device I/O buffer
* @buf_lock: Mutex to protect rx and tx buffers.
* @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
* @fs: Full scale register and full scale list available.
* @bdu: Block data update register.
* @drdy_irq: Data ready register of the sensor.
+ * @event_irq: Event register of the sensor
* @multi_read_bit: Use or not particular bit for [I2C/SPI] multi-read.
* @bootime: samples to discard when sensor passing from power-down to power-up.
*/
struct st_sensor_fullscale fs;
struct st_sensor_bdu bdu;
struct st_sensor_data_ready_irq drdy_irq;
+ struct st_sensor_event_irq event_irq;
bool multi_read_bit;
unsigned int bootime;
};
* @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
* @buffer_data: Data used by buffer part.
* @odr: Output data rate of the sensor [Hz].
+ * @events_flag: Data used by event part.
* @irq_map: Container of mapped IRQs.
* @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @get_irq_event: Function to get the IRQ used for event signal.
* @tf: Transfer function structure used by I/O operations.
* @tb: Transfer buffers and mutex used by I/O operations.
*/
char *buffer_data;
unsigned int odr;
+ unsigned int events_flag;
unsigned int irq_map[ST_SENSORS_INT_MAX];
unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+ unsigned int (*get_irq_event) (struct iio_dev *indio_dev);
const struct st_sensor_transfer_function *tf;
struct st_sensor_transfer_buffer tb;
ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr, char *buf);
+int st_sensors_read_event_config(struct iio_dev *indio_dev,
+ u64 event_code);
+
+int st_sensors_write_event_config(struct iio_dev *indio_dev,
+ u64 event_code, int state);
+
+int st_sensors_read_event_value(struct iio_dev *indio_dev,
+ u64 event_code, int *val);
+
+int st_sensors_write_event_value(struct iio_dev *indio_dev,
+ u64 event_code, int val);
+
+int st_sensors_request_event_irq(struct iio_dev *indio_dev);
+
+int st_sensors_enable_events(struct iio_dev *indio_dev);
#endif /* ST_SENSORS_H */