Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Azoteq IQS624/625 Angular Position Sensors
0004  *
0005  * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/iio/events.h>
0010 #include <linux/iio/iio.h>
0011 #include <linux/kernel.h>
0012 #include <linux/mfd/iqs62x.h>
0013 #include <linux/module.h>
0014 #include <linux/mutex.h>
0015 #include <linux/notifier.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regmap.h>
0018 
0019 #define IQS624_POS_DEG_OUT          0x16
0020 
0021 #define IQS624_POS_SCALE1           (314159 / 180)
0022 #define IQS624_POS_SCALE2           100000
0023 
0024 struct iqs624_pos_private {
0025     struct iqs62x_core *iqs62x;
0026     struct iio_dev *indio_dev;
0027     struct notifier_block notifier;
0028     struct mutex lock;
0029     bool angle_en;
0030     u16 angle;
0031 };
0032 
0033 static int iqs624_pos_angle_en(struct iqs62x_core *iqs62x, bool angle_en)
0034 {
0035     unsigned int event_mask = IQS624_HALL_UI_WHL_EVENT;
0036 
0037     /*
0038      * The IQS625 reports angular position in the form of coarse intervals,
0039      * so only interval change events are unmasked. Conversely, the IQS624
0040      * reports angular position down to one degree of resolution, so wheel
0041      * movement events are unmasked instead.
0042      */
0043     if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
0044         event_mask = IQS624_HALL_UI_INT_EVENT;
0045 
0046     return regmap_update_bits(iqs62x->regmap, IQS624_HALL_UI, event_mask,
0047                   angle_en ? 0 : 0xFF);
0048 }
0049 
0050 static int iqs624_pos_notifier(struct notifier_block *notifier,
0051                    unsigned long event_flags, void *context)
0052 {
0053     struct iqs62x_event_data *event_data = context;
0054     struct iqs624_pos_private *iqs624_pos;
0055     struct iqs62x_core *iqs62x;
0056     struct iio_dev *indio_dev;
0057     u16 angle = event_data->ui_data;
0058     s64 timestamp;
0059     int ret;
0060 
0061     iqs624_pos = container_of(notifier, struct iqs624_pos_private,
0062                   notifier);
0063     indio_dev = iqs624_pos->indio_dev;
0064     timestamp = iio_get_time_ns(indio_dev);
0065 
0066     iqs62x = iqs624_pos->iqs62x;
0067     if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
0068         angle = event_data->interval;
0069 
0070     mutex_lock(&iqs624_pos->lock);
0071 
0072     if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
0073         ret = iqs624_pos_angle_en(iqs62x, iqs624_pos->angle_en);
0074         if (ret) {
0075             dev_err(indio_dev->dev.parent,
0076                 "Failed to re-initialize device: %d\n", ret);
0077             ret = NOTIFY_BAD;
0078         } else {
0079             ret = NOTIFY_OK;
0080         }
0081     } else if (iqs624_pos->angle_en && (angle != iqs624_pos->angle)) {
0082         iio_push_event(indio_dev,
0083                    IIO_UNMOD_EVENT_CODE(IIO_ANGL, 0,
0084                             IIO_EV_TYPE_CHANGE,
0085                             IIO_EV_DIR_NONE),
0086                    timestamp);
0087 
0088         iqs624_pos->angle = angle;
0089         ret = NOTIFY_OK;
0090     } else {
0091         ret = NOTIFY_DONE;
0092     }
0093 
0094     mutex_unlock(&iqs624_pos->lock);
0095 
0096     return ret;
0097 }
0098 
0099 static void iqs624_pos_notifier_unregister(void *context)
0100 {
0101     struct iqs624_pos_private *iqs624_pos = context;
0102     struct iio_dev *indio_dev = iqs624_pos->indio_dev;
0103     int ret;
0104 
0105     ret = blocking_notifier_chain_unregister(&iqs624_pos->iqs62x->nh,
0106                          &iqs624_pos->notifier);
0107     if (ret)
0108         dev_err(indio_dev->dev.parent,
0109             "Failed to unregister notifier: %d\n", ret);
0110 }
0111 
0112 static int iqs624_pos_angle_get(struct iqs62x_core *iqs62x, unsigned int *val)
0113 {
0114     int ret;
0115     __le16 val_buf;
0116 
0117     if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
0118         return regmap_read(iqs62x->regmap, iqs62x->dev_desc->interval,
0119                    val);
0120 
0121     ret = regmap_raw_read(iqs62x->regmap, IQS624_POS_DEG_OUT, &val_buf,
0122                   sizeof(val_buf));
0123     if (ret)
0124         return ret;
0125 
0126     *val = le16_to_cpu(val_buf);
0127 
0128     return 0;
0129 }
0130 
0131 static int iqs624_pos_read_raw(struct iio_dev *indio_dev,
0132                    struct iio_chan_spec const *chan,
0133                    int *val, int *val2, long mask)
0134 {
0135     struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
0136     struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
0137     unsigned int scale = 1;
0138     int ret;
0139 
0140     switch (mask) {
0141     case IIO_CHAN_INFO_RAW:
0142         ret = iqs624_pos_angle_get(iqs62x, val);
0143         if (ret)
0144             return ret;
0145 
0146         return IIO_VAL_INT;
0147 
0148     case IIO_CHAN_INFO_SCALE:
0149         if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM) {
0150             ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV,
0151                       &scale);
0152             if (ret)
0153                 return ret;
0154         }
0155 
0156         *val = scale * IQS624_POS_SCALE1;
0157         *val2 = IQS624_POS_SCALE2;
0158         return IIO_VAL_FRACTIONAL;
0159 
0160     default:
0161         return -EINVAL;
0162     }
0163 }
0164 
0165 static int iqs624_pos_read_event_config(struct iio_dev *indio_dev,
0166                     const struct iio_chan_spec *chan,
0167                     enum iio_event_type type,
0168                     enum iio_event_direction dir)
0169 {
0170     struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
0171     int ret;
0172 
0173     mutex_lock(&iqs624_pos->lock);
0174     ret = iqs624_pos->angle_en;
0175     mutex_unlock(&iqs624_pos->lock);
0176 
0177     return ret;
0178 }
0179 
0180 static int iqs624_pos_write_event_config(struct iio_dev *indio_dev,
0181                      const struct iio_chan_spec *chan,
0182                      enum iio_event_type type,
0183                      enum iio_event_direction dir,
0184                      int state)
0185 {
0186     struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
0187     struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
0188     unsigned int val;
0189     int ret;
0190 
0191     mutex_lock(&iqs624_pos->lock);
0192 
0193     ret = iqs624_pos_angle_get(iqs62x, &val);
0194     if (ret)
0195         goto err_mutex;
0196 
0197     ret = iqs624_pos_angle_en(iqs62x, state);
0198     if (ret)
0199         goto err_mutex;
0200 
0201     iqs624_pos->angle = val;
0202     iqs624_pos->angle_en = state;
0203 
0204 err_mutex:
0205     mutex_unlock(&iqs624_pos->lock);
0206 
0207     return ret;
0208 }
0209 
0210 static const struct iio_info iqs624_pos_info = {
0211     .read_raw = &iqs624_pos_read_raw,
0212     .read_event_config = iqs624_pos_read_event_config,
0213     .write_event_config = iqs624_pos_write_event_config,
0214 };
0215 
0216 static const struct iio_event_spec iqs624_pos_events[] = {
0217     {
0218         .type = IIO_EV_TYPE_CHANGE,
0219         .dir = IIO_EV_DIR_NONE,
0220         .mask_separate = BIT(IIO_EV_INFO_ENABLE),
0221     },
0222 };
0223 
0224 static const struct iio_chan_spec iqs624_pos_channels[] = {
0225     {
0226         .type = IIO_ANGL,
0227         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0228                       BIT(IIO_CHAN_INFO_SCALE),
0229         .event_spec = iqs624_pos_events,
0230         .num_event_specs = ARRAY_SIZE(iqs624_pos_events),
0231     },
0232 };
0233 
0234 static int iqs624_pos_probe(struct platform_device *pdev)
0235 {
0236     struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
0237     struct iqs624_pos_private *iqs624_pos;
0238     struct iio_dev *indio_dev;
0239     int ret;
0240 
0241     indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs624_pos));
0242     if (!indio_dev)
0243         return -ENOMEM;
0244 
0245     iqs624_pos = iio_priv(indio_dev);
0246     iqs624_pos->iqs62x = iqs62x;
0247     iqs624_pos->indio_dev = indio_dev;
0248 
0249     indio_dev->modes = INDIO_DIRECT_MODE;
0250     indio_dev->channels = iqs624_pos_channels;
0251     indio_dev->num_channels = ARRAY_SIZE(iqs624_pos_channels);
0252     indio_dev->name = iqs62x->dev_desc->dev_name;
0253     indio_dev->info = &iqs624_pos_info;
0254 
0255     mutex_init(&iqs624_pos->lock);
0256 
0257     iqs624_pos->notifier.notifier_call = iqs624_pos_notifier;
0258     ret = blocking_notifier_chain_register(&iqs624_pos->iqs62x->nh,
0259                            &iqs624_pos->notifier);
0260     if (ret) {
0261         dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
0262         return ret;
0263     }
0264 
0265     ret = devm_add_action_or_reset(&pdev->dev,
0266                        iqs624_pos_notifier_unregister,
0267                        iqs624_pos);
0268     if (ret)
0269         return ret;
0270 
0271     return devm_iio_device_register(&pdev->dev, indio_dev);
0272 }
0273 
0274 static struct platform_driver iqs624_pos_platform_driver = {
0275     .driver = {
0276         .name = "iqs624-pos",
0277     },
0278     .probe = iqs624_pos_probe,
0279 };
0280 module_platform_driver(iqs624_pos_platform_driver);
0281 
0282 MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
0283 MODULE_DESCRIPTION("Azoteq IQS624/625 Angular Position Sensors");
0284 MODULE_LICENSE("GPL");
0285 MODULE_ALIAS("platform:iqs624-pos");