18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * itg3200_buffer.c -- support InvenSense ITG3200
48c2ecf20Sopenharmony_ci *                     Digital 3-Axis Gyroscope driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
78c2ecf20Sopenharmony_ci * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
88c2ecf20Sopenharmony_ci * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
168c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h>
178c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h>
188c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h>
208c2ecf20Sopenharmony_ci#include <linux/iio/gyro/itg3200.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	u8 tx = 0x80 | ITG3200_REG_TEMP_OUT_H;
268c2ecf20Sopenharmony_ci	struct i2c_msg msg[2] = {
278c2ecf20Sopenharmony_ci		{
288c2ecf20Sopenharmony_ci			.addr = i2c->addr,
298c2ecf20Sopenharmony_ci			.flags = i2c->flags,
308c2ecf20Sopenharmony_ci			.len = 1,
318c2ecf20Sopenharmony_ci			.buf = &tx,
328c2ecf20Sopenharmony_ci		},
338c2ecf20Sopenharmony_ci		{
348c2ecf20Sopenharmony_ci			.addr = i2c->addr,
358c2ecf20Sopenharmony_ci			.flags = i2c->flags | I2C_M_RD,
368c2ecf20Sopenharmony_ci			.len = ITG3200_SCAN_ELEMENTS * sizeof(s16),
378c2ecf20Sopenharmony_ci			.buf = (char *)&buf,
388c2ecf20Sopenharmony_ci		},
398c2ecf20Sopenharmony_ci	};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return i2c_transfer(i2c->adapter, msg, 2);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic irqreturn_t itg3200_trigger_handler(int irq, void *p)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct iio_poll_func *pf = p;
478c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = pf->indio_dev;
488c2ecf20Sopenharmony_ci	struct itg3200 *st = iio_priv(indio_dev);
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * Ensure correct alignment and padding including for the
518c2ecf20Sopenharmony_ci	 * timestamp that may be inserted.
528c2ecf20Sopenharmony_ci	 */
538c2ecf20Sopenharmony_ci	struct {
548c2ecf20Sopenharmony_ci		__be16 buf[ITG3200_SCAN_ELEMENTS];
558c2ecf20Sopenharmony_ci		s64 ts __aligned(8);
568c2ecf20Sopenharmony_ci	} scan;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	int ret = itg3200_read_all_channels(st->i2c, scan.buf);
598c2ecf20Sopenharmony_ci	if (ret < 0)
608c2ecf20Sopenharmony_ci		goto error_ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cierror_ret:
658c2ecf20Sopenharmony_ci	iio_trigger_notify_done(indio_dev->trig);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciint itg3200_buffer_configure(struct iio_dev *indio_dev)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
738c2ecf20Sopenharmony_ci		itg3200_trigger_handler, NULL);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid itg3200_buffer_unconfigure(struct iio_dev *indio_dev)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	iio_triggered_buffer_cleanup(indio_dev);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig,
838c2ecf20Sopenharmony_ci		bool state)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
868c2ecf20Sopenharmony_ci	int ret;
878c2ecf20Sopenharmony_ci	u8 msc;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, &msc);
908c2ecf20Sopenharmony_ci	if (ret)
918c2ecf20Sopenharmony_ci		goto error_ret;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (state)
948c2ecf20Sopenharmony_ci		msc |= ITG3200_IRQ_DATA_RDY_ENABLE;
958c2ecf20Sopenharmony_ci	else
968c2ecf20Sopenharmony_ci		msc &= ~ITG3200_IRQ_DATA_RDY_ENABLE;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, msc);
998c2ecf20Sopenharmony_ci	if (ret)
1008c2ecf20Sopenharmony_ci		goto error_ret;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cierror_ret:
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic const struct iio_trigger_ops itg3200_trigger_ops = {
1088c2ecf20Sopenharmony_ci	.set_trigger_state = &itg3200_data_rdy_trigger_set_state,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint itg3200_probe_trigger(struct iio_dev *indio_dev)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	int ret;
1148c2ecf20Sopenharmony_ci	struct itg3200 *st = iio_priv(indio_dev);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
1178c2ecf20Sopenharmony_ci				     indio_dev->id);
1188c2ecf20Sopenharmony_ci	if (!st->trig)
1198c2ecf20Sopenharmony_ci		return -ENOMEM;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	ret = request_irq(st->i2c->irq,
1228c2ecf20Sopenharmony_ci			  &iio_trigger_generic_data_rdy_poll,
1238c2ecf20Sopenharmony_ci			  IRQF_TRIGGER_RISING,
1248c2ecf20Sopenharmony_ci			  "itg3200_data_rdy",
1258c2ecf20Sopenharmony_ci			  st->trig);
1268c2ecf20Sopenharmony_ci	if (ret)
1278c2ecf20Sopenharmony_ci		goto error_free_trig;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	st->trig->dev.parent = &st->i2c->dev;
1318c2ecf20Sopenharmony_ci	st->trig->ops = &itg3200_trigger_ops;
1328c2ecf20Sopenharmony_ci	iio_trigger_set_drvdata(st->trig, indio_dev);
1338c2ecf20Sopenharmony_ci	ret = iio_trigger_register(st->trig);
1348c2ecf20Sopenharmony_ci	if (ret)
1358c2ecf20Sopenharmony_ci		goto error_free_irq;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* select default trigger */
1388c2ecf20Sopenharmony_ci	indio_dev->trig = iio_trigger_get(st->trig);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cierror_free_irq:
1438c2ecf20Sopenharmony_ci	free_irq(st->i2c->irq, st->trig);
1448c2ecf20Sopenharmony_cierror_free_trig:
1458c2ecf20Sopenharmony_ci	iio_trigger_free(st->trig);
1468c2ecf20Sopenharmony_ci	return ret;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid itg3200_remove_trigger(struct iio_dev *indio_dev)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct itg3200 *st = iio_priv(indio_dev);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	iio_trigger_unregister(st->trig);
1548c2ecf20Sopenharmony_ci	free_irq(st->i2c->irq, st->trig);
1558c2ecf20Sopenharmony_ci	iio_trigger_free(st->trig);
1568c2ecf20Sopenharmony_ci}
157