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