packaging: Add spec file for VisionFive
[platform/kernel/linux-starfive.git] / drivers / iio / position / iqs624-pos.c
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
24 struct 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
33 static 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
50 static 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
99 static 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
112 static 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
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)
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
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)
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
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,
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
204 err_mutex:
205         mutex_unlock(&iqs624_pos->lock);
206
207         return ret;
208 }
209
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,
214 };
215
216 static 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
224 static 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
234 static 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
274 static struct platform_driver iqs624_pos_platform_driver = {
275         .driver = {
276                 .name = "iqs624-pos",
277         },
278         .probe = iqs624_pos_probe,
279 };
280 module_platform_driver(iqs624_pos_platform_driver);
281
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");