1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Azoteq IQS624/625 Angular Position Sensors
4 *
5 * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
6 */
7
8#include <linux/device.h>
9#include <linux/iio/events.h>
10#include <linux/iio/iio.h>
11#include <linux/kernel.h>
12#include <linux/mfd/iqs62x.h>
13#include <linux/module.h>
14#include <linux/mutex.h>
15#include <linux/notifier.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18
19#define IQS624_POS_DEG_OUT			0x16
20
21#define IQS624_POS_SCALE1			(314159 / 180)
22#define IQS624_POS_SCALE2			100000
23
24struct iqs624_pos_private {
25	struct iqs62x_core *iqs62x;
26	struct iio_dev *indio_dev;
27	struct notifier_block notifier;
28	struct mutex lock;
29	bool angle_en;
30	u16 angle;
31};
32
33static int iqs624_pos_angle_en(struct iqs62x_core *iqs62x, bool angle_en)
34{
35	unsigned int event_mask = IQS624_HALL_UI_WHL_EVENT;
36
37	/*
38	 * The IQS625 reports angular position in the form of coarse intervals,
39	 * so only interval change events are unmasked. Conversely, the IQS624
40	 * reports angular position down to one degree of resolution, so wheel
41	 * movement events are unmasked instead.
42	 */
43	if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
44		event_mask = IQS624_HALL_UI_INT_EVENT;
45
46	return regmap_update_bits(iqs62x->regmap, IQS624_HALL_UI, event_mask,
47				  angle_en ? 0 : 0xFF);
48}
49
50static int iqs624_pos_notifier(struct notifier_block *notifier,
51			       unsigned long event_flags, void *context)
52{
53	struct iqs62x_event_data *event_data = context;
54	struct iqs624_pos_private *iqs624_pos;
55	struct iqs62x_core *iqs62x;
56	struct iio_dev *indio_dev;
57	u16 angle = event_data->ui_data;
58	s64 timestamp;
59	int ret;
60
61	iqs624_pos = container_of(notifier, struct iqs624_pos_private,
62				  notifier);
63	indio_dev = iqs624_pos->indio_dev;
64	timestamp = iio_get_time_ns(indio_dev);
65
66	iqs62x = iqs624_pos->iqs62x;
67	if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
68		angle = event_data->interval;
69
70	mutex_lock(&iqs624_pos->lock);
71
72	if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
73		ret = iqs624_pos_angle_en(iqs62x, iqs624_pos->angle_en);
74		if (ret) {
75			dev_err(indio_dev->dev.parent,
76				"Failed to re-initialize device: %d\n", ret);
77			ret = NOTIFY_BAD;
78		} else {
79			ret = NOTIFY_OK;
80		}
81	} else if (iqs624_pos->angle_en && (angle != iqs624_pos->angle)) {
82		iio_push_event(indio_dev,
83			       IIO_UNMOD_EVENT_CODE(IIO_ANGL, 0,
84						    IIO_EV_TYPE_CHANGE,
85						    IIO_EV_DIR_NONE),
86			       timestamp);
87
88		iqs624_pos->angle = angle;
89		ret = NOTIFY_OK;
90	} else {
91		ret = NOTIFY_DONE;
92	}
93
94	mutex_unlock(&iqs624_pos->lock);
95
96	return ret;
97}
98
99static void iqs624_pos_notifier_unregister(void *context)
100{
101	struct iqs624_pos_private *iqs624_pos = context;
102	struct iio_dev *indio_dev = iqs624_pos->indio_dev;
103	int ret;
104
105	ret = blocking_notifier_chain_unregister(&iqs624_pos->iqs62x->nh,
106						 &iqs624_pos->notifier);
107	if (ret)
108		dev_err(indio_dev->dev.parent,
109			"Failed to unregister notifier: %d\n", ret);
110}
111
112static int iqs624_pos_angle_get(struct iqs62x_core *iqs62x, unsigned int *val)
113{
114	int ret;
115	__le16 val_buf;
116
117	if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
118		return regmap_read(iqs62x->regmap, iqs62x->dev_desc->interval,
119				   val);
120
121	ret = regmap_raw_read(iqs62x->regmap, IQS624_POS_DEG_OUT, &val_buf,
122			      sizeof(val_buf));
123	if (ret)
124		return ret;
125
126	*val = le16_to_cpu(val_buf);
127
128	return 0;
129}
130
131static int iqs624_pos_read_raw(struct iio_dev *indio_dev,
132			       struct iio_chan_spec const *chan,
133			       int *val, int *val2, long mask)
134{
135	struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
136	struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
137	unsigned int scale = 1;
138	int ret;
139
140	switch (mask) {
141	case IIO_CHAN_INFO_RAW:
142		ret = iqs624_pos_angle_get(iqs62x, val);
143		if (ret)
144			return ret;
145
146		return IIO_VAL_INT;
147
148	case IIO_CHAN_INFO_SCALE:
149		if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM) {
150			ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV,
151					  &scale);
152			if (ret)
153				return ret;
154		}
155
156		*val = scale * IQS624_POS_SCALE1;
157		*val2 = IQS624_POS_SCALE2;
158		return IIO_VAL_FRACTIONAL;
159
160	default:
161		return -EINVAL;
162	}
163}
164
165static int iqs624_pos_read_event_config(struct iio_dev *indio_dev,
166					const struct iio_chan_spec *chan,
167					enum iio_event_type type,
168					enum iio_event_direction dir)
169{
170	struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
171	int ret;
172
173	mutex_lock(&iqs624_pos->lock);
174	ret = iqs624_pos->angle_en;
175	mutex_unlock(&iqs624_pos->lock);
176
177	return ret;
178}
179
180static int iqs624_pos_write_event_config(struct iio_dev *indio_dev,
181					 const struct iio_chan_spec *chan,
182					 enum iio_event_type type,
183					 enum iio_event_direction dir,
184					 int state)
185{
186	struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
187	struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
188	unsigned int val;
189	int ret;
190
191	mutex_lock(&iqs624_pos->lock);
192
193	ret = iqs624_pos_angle_get(iqs62x, &val);
194	if (ret)
195		goto err_mutex;
196
197	ret = iqs624_pos_angle_en(iqs62x, state);
198	if (ret)
199		goto err_mutex;
200
201	iqs624_pos->angle = val;
202	iqs624_pos->angle_en = state;
203
204err_mutex:
205	mutex_unlock(&iqs624_pos->lock);
206
207	return ret;
208}
209
210static const struct iio_info iqs624_pos_info = {
211	.read_raw = &iqs624_pos_read_raw,
212	.read_event_config = iqs624_pos_read_event_config,
213	.write_event_config = iqs624_pos_write_event_config,
214};
215
216static const struct iio_event_spec iqs624_pos_events[] = {
217	{
218		.type = IIO_EV_TYPE_CHANGE,
219		.dir = IIO_EV_DIR_NONE,
220		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
221	},
222};
223
224static const struct iio_chan_spec iqs624_pos_channels[] = {
225	{
226		.type = IIO_ANGL,
227		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
228				      BIT(IIO_CHAN_INFO_SCALE),
229		.event_spec = iqs624_pos_events,
230		.num_event_specs = ARRAY_SIZE(iqs624_pos_events),
231	},
232};
233
234static int iqs624_pos_probe(struct platform_device *pdev)
235{
236	struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
237	struct iqs624_pos_private *iqs624_pos;
238	struct iio_dev *indio_dev;
239	int ret;
240
241	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs624_pos));
242	if (!indio_dev)
243		return -ENOMEM;
244
245	iqs624_pos = iio_priv(indio_dev);
246	iqs624_pos->iqs62x = iqs62x;
247	iqs624_pos->indio_dev = indio_dev;
248
249	indio_dev->modes = INDIO_DIRECT_MODE;
250	indio_dev->channels = iqs624_pos_channels;
251	indio_dev->num_channels = ARRAY_SIZE(iqs624_pos_channels);
252	indio_dev->name = iqs62x->dev_desc->dev_name;
253	indio_dev->info = &iqs624_pos_info;
254
255	mutex_init(&iqs624_pos->lock);
256
257	iqs624_pos->notifier.notifier_call = iqs624_pos_notifier;
258	ret = blocking_notifier_chain_register(&iqs624_pos->iqs62x->nh,
259					       &iqs624_pos->notifier);
260	if (ret) {
261		dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
262		return ret;
263	}
264
265	ret = devm_add_action_or_reset(&pdev->dev,
266				       iqs624_pos_notifier_unregister,
267				       iqs624_pos);
268	if (ret)
269		return ret;
270
271	return devm_iio_device_register(&pdev->dev, indio_dev);
272}
273
274static struct platform_driver iqs624_pos_platform_driver = {
275	.driver = {
276		.name = "iqs624-pos",
277	},
278	.probe = iqs624_pos_probe,
279};
280module_platform_driver(iqs624_pos_platform_driver);
281
282MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
283MODULE_DESCRIPTION("Azoteq IQS624/625 Angular Position Sensors");
284MODULE_LICENSE("GPL");
285MODULE_ALIAS("platform:iqs624-pos");
286