iqs624-pos.c (7178B)
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");