Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Support for Vishay VCNL3020 proximity sensor on i2c bus.
0004  * Based on Vishay VCNL4000 driver code.
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/i2c.h>
0009 #include <linux/err.h>
0010 #include <linux/delay.h>
0011 #include <linux/regmap.h>
0012 #include <linux/interrupt.h>
0013 
0014 #include <linux/iio/iio.h>
0015 #include <linux/iio/events.h>
0016 
0017 #define VCNL3020_PROD_ID    0x21
0018 
0019 #define VCNL_COMMAND        0x80 /* Command register */
0020 #define VCNL_PROD_REV       0x81 /* Product ID and Revision ID */
0021 #define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */
0022 #define VCNL_LED_CURRENT    0x83 /* IR LED current for proximity mode */
0023 #define VCNL_PS_RESULT_HI   0x87 /* Proximity result register, MSB */
0024 #define VCNL_PS_RESULT_LO   0x88 /* Proximity result register, LSB */
0025 #define VCNL_PS_ICR     0x89 /* Interrupt Control Register */
0026 #define VCNL_PS_LO_THR_HI   0x8a /* High byte of low threshold value */
0027 #define VCNL_PS_LO_THR_LO   0x8b /* Low byte of low threshold value */
0028 #define VCNL_PS_HI_THR_HI   0x8c /* High byte of high threshold value */
0029 #define VCNL_PS_HI_THR_LO   0x8d /* Low byte of high threshold value */
0030 #define VCNL_ISR        0x8e /* Interrupt Status Register */
0031 #define VCNL_PS_MOD_ADJ     0x8f /* Proximity Modulator Timing Adjustment */
0032 
0033 /* Bit masks for COMMAND register */
0034 #define VCNL_PS_RDY     BIT(5) /* proximity data ready? */
0035 #define VCNL_PS_OD      BIT(3) /* start on-demand proximity
0036                     * measurement
0037                     */
0038 
0039 /* Enables periodic proximity measurement */
0040 #define VCNL_PS_EN      BIT(1)
0041 
0042 /* Enables state machine and LP oscillator for self timed  measurements */
0043 #define VCNL_PS_SELFTIMED_EN    BIT(0)
0044 
0045 /* Bit masks for ICR */
0046 
0047 /* Enable interrupts on low or high thresholds */
0048 #define  VCNL_ICR_THRES_EN  BIT(1)
0049 
0050 /* Bit masks for ISR */
0051 #define VCNL_INT_TH_HI      BIT(0)  /* High threshold hit */
0052 #define VCNL_INT_TH_LOW     BIT(1)  /* Low threshold hit */
0053 
0054 #define VCNL_ON_DEMAND_TIMEOUT_US   100000
0055 #define VCNL_POLL_US            20000
0056 
0057 static const int vcnl3020_prox_sampling_frequency[][2] = {
0058     {1, 950000},
0059     {3, 906250},
0060     {7, 812500},
0061     {16, 625000},
0062     {31, 250000},
0063     {62, 500000},
0064     {125, 0},
0065     {250, 0},
0066 };
0067 
0068 /**
0069  * struct vcnl3020_data - vcnl3020 specific data.
0070  * @regmap: device register map.
0071  * @dev:    vcnl3020 device.
0072  * @rev:    revision id.
0073  * @lock:   lock for protecting access to device hardware registers.
0074  * @buf:    __be16 buffer.
0075  */
0076 struct vcnl3020_data {
0077     struct regmap *regmap;
0078     struct device *dev;
0079     u8 rev;
0080     struct mutex lock;
0081     __be16 buf;
0082 };
0083 
0084 /**
0085  * struct vcnl3020_property - vcnl3020 property.
0086  * @name:   property name.
0087  * @reg:    i2c register offset.
0088  * @conversion_func:    conversion function.
0089  */
0090 struct vcnl3020_property {
0091     const char *name;
0092     u32 reg;
0093     u32 (*conversion_func)(u32 *val);
0094 };
0095 
0096 static u32 microamp_to_reg(u32 *val)
0097 {
0098     /*
0099      * An example of conversion from uA to reg val:
0100      * 200000 uA == 200 mA == 20
0101      */
0102     return *val /= 10000;
0103 };
0104 
0105 static struct vcnl3020_property vcnl3020_led_current_property = {
0106     .name = "vishay,led-current-microamp",
0107     .reg = VCNL_LED_CURRENT,
0108     .conversion_func = microamp_to_reg,
0109 };
0110 
0111 static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
0112                        struct vcnl3020_property prop)
0113 {
0114     int rc;
0115     u32 val;
0116 
0117     rc = device_property_read_u32(data->dev, prop.name, &val);
0118     if (rc)
0119         return 0;
0120 
0121     if (prop.conversion_func)
0122         prop.conversion_func(&val);
0123 
0124     rc = regmap_write(data->regmap, prop.reg, val);
0125     if (rc) {
0126         dev_err(data->dev, "Error (%d) setting property (%s)\n",
0127             rc, prop.name);
0128     }
0129 
0130     return rc;
0131 }
0132 
0133 static int vcnl3020_init(struct vcnl3020_data *data)
0134 {
0135     int rc;
0136     unsigned int reg;
0137 
0138     rc = regmap_read(data->regmap, VCNL_PROD_REV, &reg);
0139     if (rc) {
0140         dev_err(data->dev,
0141             "Error (%d) reading product revision\n", rc);
0142         return rc;
0143     }
0144 
0145     if (reg != VCNL3020_PROD_ID) {
0146         dev_err(data->dev,
0147             "Product id (%x) did not match vcnl3020 (%x)\n", reg,
0148             VCNL3020_PROD_ID);
0149         return -ENODEV;
0150     }
0151 
0152     data->rev = reg;
0153     mutex_init(&data->lock);
0154 
0155     return vcnl3020_get_and_apply_property(data,
0156                            vcnl3020_led_current_property);
0157 };
0158 
0159 static bool vcnl3020_is_in_periodic_mode(struct vcnl3020_data *data)
0160 {
0161     int rc;
0162     unsigned int cmd;
0163 
0164     rc = regmap_read(data->regmap, VCNL_COMMAND, &cmd);
0165     if (rc) {
0166         dev_err(data->dev,
0167             "Error (%d) reading command register\n", rc);
0168         return false;
0169     }
0170 
0171     return !!(cmd & VCNL_PS_SELFTIMED_EN);
0172 }
0173 
0174 static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
0175 {
0176     int rc;
0177     unsigned int reg;
0178 
0179     mutex_lock(&data->lock);
0180 
0181     /* Protect against event capture. */
0182     if (vcnl3020_is_in_periodic_mode(data)) {
0183         rc = -EBUSY;
0184         goto err_unlock;
0185     }
0186 
0187     rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
0188     if (rc)
0189         goto err_unlock;
0190 
0191     /* wait for data to become ready */
0192     rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg,
0193                       reg & VCNL_PS_RDY, VCNL_POLL_US,
0194                       VCNL_ON_DEMAND_TIMEOUT_US);
0195     if (rc) {
0196         dev_err(data->dev,
0197             "Error (%d) reading vcnl3020 command register\n", rc);
0198         goto err_unlock;
0199     }
0200 
0201     /* high & low result bytes read */
0202     rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &data->buf,
0203                   sizeof(data->buf));
0204     if (rc)
0205         goto err_unlock;
0206 
0207     *val = be16_to_cpu(data->buf);
0208 
0209 err_unlock:
0210     mutex_unlock(&data->lock);
0211 
0212     return rc;
0213 }
0214 
0215 static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val,
0216                      int *val2)
0217 {
0218     int rc;
0219     unsigned int prox_rate;
0220 
0221     rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate);
0222     if (rc)
0223         return rc;
0224 
0225     if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency))
0226         return -EINVAL;
0227 
0228     *val = vcnl3020_prox_sampling_frequency[prox_rate][0];
0229     *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1];
0230 
0231     return 0;
0232 }
0233 
0234 static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val,
0235                       int val2)
0236 {
0237     unsigned int i;
0238     int index = -1;
0239     int rc;
0240 
0241     mutex_lock(&data->lock);
0242 
0243     /* Protect against event capture. */
0244     if (vcnl3020_is_in_periodic_mode(data)) {
0245         rc = -EBUSY;
0246         goto err_unlock;
0247     }
0248 
0249     for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) {
0250         if (val == vcnl3020_prox_sampling_frequency[i][0] &&
0251             val2 == vcnl3020_prox_sampling_frequency[i][1]) {
0252             index = i;
0253             break;
0254         }
0255     }
0256 
0257     if (index < 0) {
0258         rc = -EINVAL;
0259         goto err_unlock;
0260     }
0261 
0262     rc = regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index);
0263     if (rc)
0264         dev_err(data->dev,
0265             "Error (%d) writing proximity rate register\n", rc);
0266 
0267 err_unlock:
0268     mutex_unlock(&data->lock);
0269 
0270     return rc;
0271 }
0272 
0273 static bool vcnl3020_is_thr_enabled(struct vcnl3020_data *data)
0274 {
0275     int rc;
0276     unsigned int icr;
0277 
0278     rc = regmap_read(data->regmap, VCNL_PS_ICR, &icr);
0279     if (rc) {
0280         dev_err(data->dev,
0281             "Error (%d) reading ICR register\n", rc);
0282         return false;
0283     }
0284 
0285     return !!(icr & VCNL_ICR_THRES_EN);
0286 }
0287 
0288 static int vcnl3020_read_event(struct iio_dev *indio_dev,
0289                    const struct iio_chan_spec *chan,
0290                    enum iio_event_type type,
0291                    enum iio_event_direction dir,
0292                    enum iio_event_info info,
0293                    int *val, int *val2)
0294 {
0295     int rc;
0296     struct vcnl3020_data *data = iio_priv(indio_dev);
0297 
0298     switch (info) {
0299     case IIO_EV_INFO_VALUE:
0300         switch (dir) {
0301         case IIO_EV_DIR_RISING:
0302             rc = regmap_bulk_read(data->regmap, VCNL_PS_HI_THR_HI,
0303                           &data->buf, sizeof(data->buf));
0304             if (rc < 0)
0305                 return rc;
0306             *val = be16_to_cpu(data->buf);
0307             return IIO_VAL_INT;
0308         case IIO_EV_DIR_FALLING:
0309             rc = regmap_bulk_read(data->regmap, VCNL_PS_LO_THR_HI,
0310                           &data->buf, sizeof(data->buf));
0311             if (rc < 0)
0312                 return rc;
0313             *val = be16_to_cpu(data->buf);
0314             return IIO_VAL_INT;
0315         default:
0316             return -EINVAL;
0317         }
0318     default:
0319         return -EINVAL;
0320     }
0321 }
0322 
0323 static int vcnl3020_write_event(struct iio_dev *indio_dev,
0324                 const struct iio_chan_spec *chan,
0325                 enum iio_event_type type,
0326                 enum iio_event_direction dir,
0327                 enum iio_event_info info,
0328                 int val, int val2)
0329 {
0330     int rc;
0331     struct vcnl3020_data *data = iio_priv(indio_dev);
0332 
0333     mutex_lock(&data->lock);
0334 
0335     switch (info) {
0336     case IIO_EV_INFO_VALUE:
0337         switch (dir) {
0338         case IIO_EV_DIR_RISING:
0339             /* 16 bit word/ low * high */
0340             data->buf = cpu_to_be16(val);
0341             rc = regmap_bulk_write(data->regmap, VCNL_PS_HI_THR_HI,
0342                            &data->buf, sizeof(data->buf));
0343             if (rc < 0)
0344                 goto err_unlock;
0345             rc = IIO_VAL_INT;
0346             goto err_unlock;
0347         case IIO_EV_DIR_FALLING:
0348             data->buf = cpu_to_be16(val);
0349             rc = regmap_bulk_write(data->regmap, VCNL_PS_LO_THR_HI,
0350                            &data->buf, sizeof(data->buf));
0351             if (rc < 0)
0352                 goto err_unlock;
0353             rc = IIO_VAL_INT;
0354             goto err_unlock;
0355         default:
0356             rc = -EINVAL;
0357             goto err_unlock;
0358         }
0359     default:
0360         rc = -EINVAL;
0361         goto err_unlock;
0362     }
0363 err_unlock:
0364     mutex_unlock(&data->lock);
0365 
0366     return rc;
0367 }
0368 
0369 static int vcnl3020_enable_periodic(struct iio_dev *indio_dev,
0370                     struct vcnl3020_data *data)
0371 {
0372     int rc;
0373     int cmd;
0374 
0375     mutex_lock(&data->lock);
0376 
0377     /* Enable periodic measurement of proximity data. */
0378     cmd = VCNL_PS_EN | VCNL_PS_SELFTIMED_EN;
0379 
0380     rc = regmap_write(data->regmap, VCNL_COMMAND, cmd);
0381     if (rc) {
0382         dev_err(data->dev,
0383             "Error (%d) writing command register\n", rc);
0384         goto err_unlock;
0385     }
0386 
0387     /*
0388      * Enable interrupts on threshold, for proximity data by
0389      * default.
0390      */
0391     rc = regmap_write(data->regmap, VCNL_PS_ICR, VCNL_ICR_THRES_EN);
0392     if (rc)
0393         dev_err(data->dev,
0394             "Error (%d) reading ICR register\n", rc);
0395 
0396 err_unlock:
0397     mutex_unlock(&data->lock);
0398 
0399     return rc;
0400 }
0401 
0402 static int vcnl3020_disable_periodic(struct iio_dev *indio_dev,
0403                      struct vcnl3020_data *data)
0404 {
0405     int rc;
0406 
0407     mutex_lock(&data->lock);
0408 
0409     rc = regmap_write(data->regmap, VCNL_COMMAND, 0);
0410     if (rc) {
0411         dev_err(data->dev,
0412             "Error (%d) writing command register\n", rc);
0413         goto err_unlock;
0414     }
0415 
0416     rc = regmap_write(data->regmap, VCNL_PS_ICR, 0);
0417     if (rc) {
0418         dev_err(data->dev,
0419             "Error (%d) writing ICR register\n", rc);
0420         goto err_unlock;
0421     }
0422 
0423     /* Clear interrupt flag bit */
0424     rc = regmap_write(data->regmap, VCNL_ISR, 0);
0425     if (rc)
0426         dev_err(data->dev,
0427             "Error (%d) writing ISR register\n", rc);
0428 
0429 err_unlock:
0430     mutex_unlock(&data->lock);
0431 
0432     return rc;
0433 }
0434 
0435 static int vcnl3020_config_threshold(struct iio_dev *indio_dev, bool state)
0436 {
0437     struct vcnl3020_data *data = iio_priv(indio_dev);
0438 
0439     if (state) {
0440         return vcnl3020_enable_periodic(indio_dev, data);
0441     } else {
0442         if (!vcnl3020_is_thr_enabled(data))
0443             return 0;
0444         return vcnl3020_disable_periodic(indio_dev, data);
0445     }
0446 }
0447 
0448 static int vcnl3020_write_event_config(struct iio_dev *indio_dev,
0449                        const struct iio_chan_spec *chan,
0450                        enum iio_event_type type,
0451                        enum iio_event_direction dir,
0452                        int state)
0453 {
0454     switch (chan->type) {
0455     case IIO_PROXIMITY:
0456         return vcnl3020_config_threshold(indio_dev, state);
0457     default:
0458         return -EINVAL;
0459     }
0460 }
0461 
0462 static int vcnl3020_read_event_config(struct iio_dev *indio_dev,
0463                       const struct iio_chan_spec *chan,
0464                       enum iio_event_type type,
0465                       enum iio_event_direction dir)
0466 {
0467     struct vcnl3020_data *data = iio_priv(indio_dev);
0468 
0469     switch (chan->type) {
0470     case IIO_PROXIMITY:
0471         return vcnl3020_is_thr_enabled(data);
0472     default:
0473         return -EINVAL;
0474     }
0475 }
0476 
0477 static const struct iio_event_spec vcnl3020_event_spec[] = {
0478     {
0479         .type = IIO_EV_TYPE_THRESH,
0480         .dir = IIO_EV_DIR_RISING,
0481         .mask_separate = BIT(IIO_EV_INFO_VALUE),
0482     }, {
0483         .type = IIO_EV_TYPE_THRESH,
0484         .dir = IIO_EV_DIR_FALLING,
0485         .mask_separate = BIT(IIO_EV_INFO_VALUE),
0486     }, {
0487         .type = IIO_EV_TYPE_THRESH,
0488         .dir = IIO_EV_DIR_EITHER,
0489         .mask_separate = BIT(IIO_EV_INFO_ENABLE),
0490     },
0491 };
0492 
0493 static const struct iio_chan_spec vcnl3020_channels[] = {
0494     {
0495         .type = IIO_PROXIMITY,
0496         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0497                       BIT(IIO_CHAN_INFO_SAMP_FREQ),
0498         .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
0499         .event_spec = vcnl3020_event_spec,
0500         .num_event_specs = ARRAY_SIZE(vcnl3020_event_spec),
0501     },
0502 };
0503 
0504 static int vcnl3020_read_raw(struct iio_dev *indio_dev,
0505                  struct iio_chan_spec const *chan, int *val,
0506                  int *val2, long mask)
0507 {
0508     int rc;
0509     struct vcnl3020_data *data = iio_priv(indio_dev);
0510 
0511     switch (mask) {
0512     case IIO_CHAN_INFO_RAW:
0513         rc = vcnl3020_measure_proximity(data, val);
0514         if (rc)
0515             return rc;
0516         return IIO_VAL_INT;
0517     case IIO_CHAN_INFO_SAMP_FREQ:
0518         rc = vcnl3020_read_proxy_samp_freq(data, val, val2);
0519         if (rc < 0)
0520             return rc;
0521         return IIO_VAL_INT_PLUS_MICRO;
0522     default:
0523         return -EINVAL;
0524     }
0525 }
0526 
0527 static int vcnl3020_write_raw(struct iio_dev *indio_dev,
0528                   struct iio_chan_spec const *chan,
0529                   int val, int val2, long mask)
0530 {
0531     struct vcnl3020_data *data = iio_priv(indio_dev);
0532 
0533     switch (mask) {
0534     case IIO_CHAN_INFO_SAMP_FREQ:
0535         return vcnl3020_write_proxy_samp_freq(data, val, val2);
0536     default:
0537         return -EINVAL;
0538     }
0539 }
0540 
0541 static int vcnl3020_read_avail(struct iio_dev *indio_dev,
0542                    struct iio_chan_spec const *chan,
0543                    const int **vals, int *type, int *length,
0544                    long mask)
0545 {
0546     switch (mask) {
0547     case IIO_CHAN_INFO_SAMP_FREQ:
0548         *vals = (int *)vcnl3020_prox_sampling_frequency;
0549         *type = IIO_VAL_INT_PLUS_MICRO;
0550         *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency);
0551         return IIO_AVAIL_LIST;
0552     default:
0553         return -EINVAL;
0554     }
0555 }
0556 
0557 static const struct iio_info vcnl3020_info = {
0558     .read_raw = vcnl3020_read_raw,
0559     .write_raw = vcnl3020_write_raw,
0560     .read_avail = vcnl3020_read_avail,
0561     .read_event_value = vcnl3020_read_event,
0562     .write_event_value = vcnl3020_write_event,
0563     .read_event_config = vcnl3020_read_event_config,
0564     .write_event_config = vcnl3020_write_event_config,
0565 };
0566 
0567 static const struct regmap_config vcnl3020_regmap_config = {
0568     .reg_bits   = 8,
0569     .val_bits   = 8,
0570     .max_register   = VCNL_PS_MOD_ADJ,
0571 };
0572 
0573 static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p)
0574 {
0575     struct iio_dev *indio_dev = p;
0576     struct vcnl3020_data *data = iio_priv(indio_dev);
0577     unsigned int isr;
0578     int rc;
0579 
0580     rc = regmap_read(data->regmap, VCNL_ISR, &isr);
0581     if (rc) {
0582         dev_err(data->dev, "Error (%d) reading reg (0x%x)\n",
0583             rc, VCNL_ISR);
0584         return IRQ_HANDLED;
0585     }
0586 
0587     if (!(isr & VCNL_ICR_THRES_EN))
0588         return IRQ_NONE;
0589 
0590     iio_push_event(indio_dev,
0591                IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
0592                             IIO_EV_TYPE_THRESH,
0593                             IIO_EV_DIR_RISING),
0594                iio_get_time_ns(indio_dev));
0595 
0596     rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN);
0597     if (rc)
0598         dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n",
0599             rc, VCNL_ISR);
0600 
0601     return IRQ_HANDLED;
0602 }
0603 
0604 static int vcnl3020_probe(struct i2c_client *client)
0605 {
0606     struct vcnl3020_data *data;
0607     struct iio_dev *indio_dev;
0608     struct regmap *regmap;
0609     int rc;
0610 
0611     regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config);
0612     if (IS_ERR(regmap)) {
0613         dev_err(&client->dev, "regmap_init failed\n");
0614         return PTR_ERR(regmap);
0615     }
0616 
0617     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0618     if (!indio_dev)
0619         return -ENOMEM;
0620 
0621     data = iio_priv(indio_dev);
0622     i2c_set_clientdata(client, indio_dev);
0623     data->regmap = regmap;
0624     data->dev = &client->dev;
0625 
0626     rc = vcnl3020_init(data);
0627     if (rc)
0628         return rc;
0629 
0630     indio_dev->info = &vcnl3020_info;
0631     indio_dev->channels = vcnl3020_channels;
0632     indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels);
0633     indio_dev->name = "vcnl3020";
0634     indio_dev->modes = INDIO_DIRECT_MODE;
0635 
0636     if (client->irq) {
0637         rc = devm_request_threaded_irq(&client->dev, client->irq,
0638                            NULL, vcnl3020_handle_irq_thread,
0639                            IRQF_ONESHOT, indio_dev->name,
0640                            indio_dev);
0641         if (rc) {
0642             dev_err(&client->dev,
0643                 "Error (%d) irq request failed (%u)\n", rc,
0644                 client->irq);
0645             return rc;
0646         }
0647     }
0648 
0649     return devm_iio_device_register(&client->dev, indio_dev);
0650 }
0651 
0652 static const struct of_device_id vcnl3020_of_match[] = {
0653     {
0654         .compatible = "vishay,vcnl3020",
0655     },
0656     {}
0657 };
0658 MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
0659 
0660 static struct i2c_driver vcnl3020_driver = {
0661     .driver = {
0662         .name   = "vcnl3020",
0663         .of_match_table = vcnl3020_of_match,
0664     },
0665     .probe_new  = vcnl3020_probe,
0666 };
0667 module_i2c_driver(vcnl3020_driver);
0668 
0669 MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
0670 MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
0671 MODULE_LICENSE("GPL");