1 // SPDX-License-Identifier: GPL-2.0+
3 * Azoteq IQS624/625 Angular Position Sensors
5 * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
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>
19 #define IQS624_POS_DEG_OUT 0x16
21 #define IQS624_POS_SCALE1 (314159 / 180)
22 #define IQS624_POS_SCALE2 100000
24 struct iqs624_pos_private
{
25 struct iqs62x_core
*iqs62x
;
26 struct iio_dev
*indio_dev
;
27 struct notifier_block notifier
;
33 static int iqs624_pos_angle_en(struct iqs62x_core
*iqs62x
, bool angle_en
)
35 unsigned int event_mask
= IQS624_HALL_UI_WHL_EVENT
;
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.
43 if (iqs62x
->dev_desc
->prod_num
== IQS625_PROD_NUM
)
44 event_mask
= IQS624_HALL_UI_INT_EVENT
;
46 return regmap_update_bits(iqs62x
->regmap
, IQS624_HALL_UI
, event_mask
,
50 static int iqs624_pos_notifier(struct notifier_block
*notifier
,
51 unsigned long event_flags
, void *context
)
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
;
61 iqs624_pos
= container_of(notifier
, struct iqs624_pos_private
,
63 indio_dev
= iqs624_pos
->indio_dev
;
64 timestamp
= iio_get_time_ns(indio_dev
);
66 iqs62x
= iqs624_pos
->iqs62x
;
67 if (iqs62x
->dev_desc
->prod_num
== IQS625_PROD_NUM
)
68 angle
= event_data
->interval
;
70 mutex_lock(&iqs624_pos
->lock
);
72 if (event_flags
& BIT(IQS62X_EVENT_SYS_RESET
)) {
73 ret
= iqs624_pos_angle_en(iqs62x
, iqs624_pos
->angle_en
);
75 dev_err(indio_dev
->dev
.parent
,
76 "Failed to re-initialize device: %d\n", ret
);
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,
88 iqs624_pos
->angle
= angle
;
94 mutex_unlock(&iqs624_pos
->lock
);
99 static void iqs624_pos_notifier_unregister(void *context
)
101 struct iqs624_pos_private
*iqs624_pos
= context
;
102 struct iio_dev
*indio_dev
= iqs624_pos
->indio_dev
;
105 ret
= blocking_notifier_chain_unregister(&iqs624_pos
->iqs62x
->nh
,
106 &iqs624_pos
->notifier
);
108 dev_err(indio_dev
->dev
.parent
,
109 "Failed to unregister notifier: %d\n", ret
);
112 static int iqs624_pos_angle_get(struct iqs62x_core
*iqs62x
, unsigned int *val
)
117 if (iqs62x
->dev_desc
->prod_num
== IQS625_PROD_NUM
)
118 return regmap_read(iqs62x
->regmap
, iqs62x
->dev_desc
->interval
,
121 ret
= regmap_raw_read(iqs62x
->regmap
, IQS624_POS_DEG_OUT
, &val_buf
,
126 *val
= le16_to_cpu(val_buf
);
131 static int iqs624_pos_read_raw(struct iio_dev
*indio_dev
,
132 struct iio_chan_spec
const *chan
,
133 int *val
, int *val2
, long mask
)
135 struct iqs624_pos_private
*iqs624_pos
= iio_priv(indio_dev
);
136 struct iqs62x_core
*iqs62x
= iqs624_pos
->iqs62x
;
137 unsigned int scale
= 1;
141 case IIO_CHAN_INFO_RAW
:
142 ret
= iqs624_pos_angle_get(iqs62x
, val
);
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
,
156 *val
= scale
* IQS624_POS_SCALE1
;
157 *val2
= IQS624_POS_SCALE2
;
158 return IIO_VAL_FRACTIONAL
;
165 static 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
)
170 struct iqs624_pos_private
*iqs624_pos
= iio_priv(indio_dev
);
173 mutex_lock(&iqs624_pos
->lock
);
174 ret
= iqs624_pos
->angle_en
;
175 mutex_unlock(&iqs624_pos
->lock
);
180 static 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
,
186 struct iqs624_pos_private
*iqs624_pos
= iio_priv(indio_dev
);
187 struct iqs62x_core
*iqs62x
= iqs624_pos
->iqs62x
;
191 mutex_lock(&iqs624_pos
->lock
);
193 ret
= iqs624_pos_angle_get(iqs62x
, &val
);
197 ret
= iqs624_pos_angle_en(iqs62x
, state
);
201 iqs624_pos
->angle
= val
;
202 iqs624_pos
->angle_en
= state
;
205 mutex_unlock(&iqs624_pos
->lock
);
210 static 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
,
216 static const struct iio_event_spec iqs624_pos_events
[] = {
218 .type
= IIO_EV_TYPE_CHANGE
,
219 .dir
= IIO_EV_DIR_NONE
,
220 .mask_separate
= BIT(IIO_EV_INFO_ENABLE
),
224 static const struct iio_chan_spec iqs624_pos_channels
[] = {
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
),
234 static int iqs624_pos_probe(struct platform_device
*pdev
)
236 struct iqs62x_core
*iqs62x
= dev_get_drvdata(pdev
->dev
.parent
);
237 struct iqs624_pos_private
*iqs624_pos
;
238 struct iio_dev
*indio_dev
;
241 indio_dev
= devm_iio_device_alloc(&pdev
->dev
, sizeof(*iqs624_pos
));
245 iqs624_pos
= iio_priv(indio_dev
);
246 iqs624_pos
->iqs62x
= iqs62x
;
247 iqs624_pos
->indio_dev
= indio_dev
;
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
;
255 mutex_init(&iqs624_pos
->lock
);
257 iqs624_pos
->notifier
.notifier_call
= iqs624_pos_notifier
;
258 ret
= blocking_notifier_chain_register(&iqs624_pos
->iqs62x
->nh
,
259 &iqs624_pos
->notifier
);
261 dev_err(&pdev
->dev
, "Failed to register notifier: %d\n", ret
);
265 ret
= devm_add_action_or_reset(&pdev
->dev
,
266 iqs624_pos_notifier_unregister
,
271 return devm_iio_device_register(&pdev
->dev
, indio_dev
);
274 static struct platform_driver iqs624_pos_platform_driver
= {
276 .name
= "iqs624-pos",
278 .probe
= iqs624_pos_probe
,
280 module_platform_driver(iqs624_pos_platform_driver
);
282 MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
283 MODULE_DESCRIPTION("Azoteq IQS624/625 Angular Position Sensors");
284 MODULE_LICENSE("GPL");
285 MODULE_ALIAS("platform:iqs624-pos");