Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * CM3605 Ambient Light and Proximity Sensor
0004  *
0005  * Copyright (C) 2016 Linaro Ltd.
0006  * Author: Linus Walleij <linus.walleij@linaro.org>
0007  *
0008  * This hardware was found in the very first Nexus One handset from Google/HTC
0009  * and an early endavour into mobile light and proximity sensors.
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/mod_devicetable.h>
0014 #include <linux/iio/iio.h>
0015 #include <linux/iio/sysfs.h>
0016 #include <linux/iio/events.h>
0017 #include <linux/iio/consumer.h> /* To get our ADC channel */
0018 #include <linux/iio/types.h> /* To deal with our ADC channel */
0019 #include <linux/init.h>
0020 #include <linux/leds.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/property.h>
0023 #include <linux/regulator/consumer.h>
0024 #include <linux/gpio/consumer.h>
0025 #include <linux/interrupt.h>
0026 #include <linux/math64.h>
0027 #include <linux/pm.h>
0028 
0029 #define CM3605_PROX_CHANNEL 0
0030 #define CM3605_ALS_CHANNEL 1
0031 #define CM3605_AOUT_TYP_MAX_MV 1550
0032 /* It should not go above 1.650V according to the data sheet */
0033 #define CM3605_AOUT_MAX_MV 1650
0034 
0035 /**
0036  * struct cm3605 - CM3605 state
0037  * @dev: pointer to parent device
0038  * @vdd: regulator controlling VDD
0039  * @aset: sleep enable GPIO, high = sleep
0040  * @aout: IIO ADC channel to convert the AOUT signal
0041  * @als_max: maximum LUX detection (depends on RSET)
0042  * @dir: proximity direction: start as FALLING
0043  * @led: trigger for the infrared LED used by the proximity sensor
0044  */
0045 struct cm3605 {
0046     struct device *dev;
0047     struct regulator *vdd;
0048     struct gpio_desc *aset;
0049     struct iio_channel *aout;
0050     s32 als_max;
0051     enum iio_event_direction dir;
0052     struct led_trigger *led;
0053 };
0054 
0055 static irqreturn_t cm3605_prox_irq(int irq, void *d)
0056 {
0057     struct iio_dev *indio_dev = d;
0058     struct cm3605 *cm3605 = iio_priv(indio_dev);
0059     u64 ev;
0060 
0061     ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, CM3605_PROX_CHANNEL,
0062                   IIO_EV_TYPE_THRESH, cm3605->dir);
0063     iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
0064 
0065     /* Invert the edge for each event */
0066     if (cm3605->dir == IIO_EV_DIR_RISING)
0067         cm3605->dir = IIO_EV_DIR_FALLING;
0068     else
0069         cm3605->dir = IIO_EV_DIR_RISING;
0070 
0071     return IRQ_HANDLED;
0072 }
0073 
0074 static int cm3605_get_lux(struct cm3605 *cm3605)
0075 {
0076     int ret, res;
0077     s64 lux;
0078 
0079     ret = iio_read_channel_processed(cm3605->aout, &res);
0080     if (ret < 0)
0081         return ret;
0082 
0083     dev_dbg(cm3605->dev, "read %d mV from ADC\n", res);
0084 
0085     /*
0086      * AOUT has an offset of ~30mV then linear at dark
0087      * then goes from 2.54 up to 650 LUX yielding 1.55V
0088      * (1550 mV) so scale the returned value to this interval
0089      * using simple linear interpolation.
0090      */
0091     if (res < 30)
0092         return 0;
0093     if (res > CM3605_AOUT_MAX_MV)
0094         dev_err(cm3605->dev, "device out of range\n");
0095 
0096     /* Remove bias */
0097     lux = res - 30;
0098 
0099     /* Linear interpolation between 0 and ALS typ max */
0100     lux *= cm3605->als_max;
0101     lux = div64_s64(lux, CM3605_AOUT_TYP_MAX_MV);
0102 
0103     return lux;
0104 }
0105 
0106 static int cm3605_read_raw(struct iio_dev *indio_dev,
0107                struct iio_chan_spec const *chan,
0108                int *val, int *val2, long mask)
0109 {
0110     struct cm3605 *cm3605 = iio_priv(indio_dev);
0111     int ret;
0112 
0113     switch (mask) {
0114     case IIO_CHAN_INFO_RAW:
0115         switch (chan->type) {
0116         case IIO_LIGHT:
0117             ret = cm3605_get_lux(cm3605);
0118             if (ret < 0)
0119                 return ret;
0120             *val = ret;
0121             return IIO_VAL_INT;
0122         default:
0123             return -EINVAL;
0124         }
0125     default:
0126         return -EINVAL;
0127     }
0128 }
0129 
0130 static const struct iio_info cm3605_info = {
0131     .read_raw = cm3605_read_raw,
0132 };
0133 
0134 static const struct iio_event_spec cm3605_events[] = {
0135     {
0136         .type = IIO_EV_TYPE_THRESH,
0137         .dir = IIO_EV_DIR_EITHER,
0138         .mask_separate = BIT(IIO_EV_INFO_ENABLE),
0139     },
0140 };
0141 
0142 static const struct iio_chan_spec cm3605_channels[] = {
0143     {
0144         .type = IIO_PROXIMITY,
0145         .event_spec = cm3605_events,
0146         .num_event_specs = ARRAY_SIZE(cm3605_events),
0147     },
0148     {
0149         .type = IIO_LIGHT,
0150         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0151         .channel = CM3605_ALS_CHANNEL,
0152     },
0153 };
0154 
0155 static int cm3605_probe(struct platform_device *pdev)
0156 {
0157     struct cm3605 *cm3605;
0158     struct iio_dev *indio_dev;
0159     struct device *dev = &pdev->dev;
0160     enum iio_chan_type ch_type;
0161     u32 rset;
0162     int irq;
0163     int ret;
0164 
0165     indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605));
0166     if (!indio_dev)
0167         return -ENOMEM;
0168     platform_set_drvdata(pdev, indio_dev);
0169 
0170     cm3605 = iio_priv(indio_dev);
0171     cm3605->dev = dev;
0172     cm3605->dir = IIO_EV_DIR_FALLING;
0173 
0174     ret = device_property_read_u32(dev, "capella,aset-resistance-ohms", &rset);
0175     if (ret) {
0176         dev_info(dev, "no RSET specified, assuming 100K\n");
0177         rset = 100000;
0178     }
0179     switch (rset) {
0180     case 50000:
0181         cm3605->als_max = 650;
0182         break;
0183     case 100000:
0184         cm3605->als_max = 300;
0185         break;
0186     case 300000:
0187         cm3605->als_max = 100;
0188         break;
0189     case 600000:
0190         cm3605->als_max = 50;
0191         break;
0192     default:
0193         dev_info(dev, "non-standard resistance\n");
0194         return -EINVAL;
0195     }
0196 
0197     cm3605->aout = devm_iio_channel_get(dev, "aout");
0198     if (IS_ERR(cm3605->aout)) {
0199         ret = PTR_ERR(cm3605->aout);
0200         ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret;
0201         return dev_err_probe(dev, ret, "failed to get AOUT ADC channel\n");
0202     }
0203     ret = iio_get_channel_type(cm3605->aout, &ch_type);
0204     if (ret < 0)
0205         return ret;
0206     if (ch_type != IIO_VOLTAGE) {
0207         dev_err(dev, "wrong type of IIO channel specified for AOUT\n");
0208         return -EINVAL;
0209     }
0210 
0211     cm3605->vdd = devm_regulator_get(dev, "vdd");
0212     if (IS_ERR(cm3605->vdd))
0213         return dev_err_probe(dev, PTR_ERR(cm3605->vdd),
0214                      "failed to get VDD regulator\n");
0215 
0216     ret = regulator_enable(cm3605->vdd);
0217     if (ret) {
0218         dev_err(dev, "failed to enable VDD regulator\n");
0219         return ret;
0220     }
0221 
0222     cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH);
0223     if (IS_ERR(cm3605->aset)) {
0224         ret = dev_err_probe(dev, PTR_ERR(cm3605->aset), "no ASET GPIO\n");
0225         goto out_disable_vdd;
0226     }
0227 
0228     irq = platform_get_irq(pdev, 0);
0229     if (irq < 0) {
0230         ret = dev_err_probe(dev, irq, "failed to get irq\n");
0231         goto out_disable_aset;
0232     }
0233 
0234     ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq,
0235                     NULL, 0, "cm3605", indio_dev);
0236     if (ret) {
0237         dev_err(dev, "unable to request IRQ\n");
0238         goto out_disable_aset;
0239     }
0240 
0241     /* Just name the trigger the same as the driver */
0242     led_trigger_register_simple("cm3605", &cm3605->led);
0243     led_trigger_event(cm3605->led, LED_FULL);
0244 
0245     indio_dev->info = &cm3605_info;
0246     indio_dev->name = "cm3605";
0247     indio_dev->channels = cm3605_channels;
0248     indio_dev->num_channels = ARRAY_SIZE(cm3605_channels);
0249     indio_dev->modes = INDIO_DIRECT_MODE;
0250 
0251     ret = iio_device_register(indio_dev);
0252     if (ret)
0253         goto out_remove_trigger;
0254     dev_info(dev, "Capella Microsystems CM3605 enabled range 0..%d LUX\n",
0255          cm3605->als_max);
0256 
0257     return 0;
0258 
0259 out_remove_trigger:
0260     led_trigger_event(cm3605->led, LED_OFF);
0261     led_trigger_unregister_simple(cm3605->led);
0262 out_disable_aset:
0263     gpiod_set_value_cansleep(cm3605->aset, 0);
0264 out_disable_vdd:
0265     regulator_disable(cm3605->vdd);
0266     return ret;
0267 }
0268 
0269 static int cm3605_remove(struct platform_device *pdev)
0270 {
0271     struct iio_dev *indio_dev = platform_get_drvdata(pdev);
0272     struct cm3605 *cm3605 = iio_priv(indio_dev);
0273 
0274     led_trigger_event(cm3605->led, LED_OFF);
0275     led_trigger_unregister_simple(cm3605->led);
0276     gpiod_set_value_cansleep(cm3605->aset, 0);
0277     iio_device_unregister(indio_dev);
0278     regulator_disable(cm3605->vdd);
0279 
0280     return 0;
0281 }
0282 
0283 static int cm3605_pm_suspend(struct device *dev)
0284 {
0285     struct iio_dev *indio_dev = dev_get_drvdata(dev);
0286     struct cm3605 *cm3605 = iio_priv(indio_dev);
0287 
0288     led_trigger_event(cm3605->led, LED_OFF);
0289     regulator_disable(cm3605->vdd);
0290 
0291     return 0;
0292 }
0293 
0294 static int cm3605_pm_resume(struct device *dev)
0295 {
0296     struct iio_dev *indio_dev = dev_get_drvdata(dev);
0297     struct cm3605 *cm3605 = iio_priv(indio_dev);
0298     int ret;
0299 
0300     ret = regulator_enable(cm3605->vdd);
0301     if (ret)
0302         dev_err(dev, "failed to enable regulator in resume path\n");
0303     led_trigger_event(cm3605->led, LED_FULL);
0304 
0305     return 0;
0306 }
0307 static DEFINE_SIMPLE_DEV_PM_OPS(cm3605_dev_pm_ops, cm3605_pm_suspend,
0308                 cm3605_pm_resume);
0309 
0310 static const struct of_device_id cm3605_of_match[] = {
0311     {.compatible = "capella,cm3605"},
0312     { },
0313 };
0314 MODULE_DEVICE_TABLE(of, cm3605_of_match);
0315 
0316 static struct platform_driver cm3605_driver = {
0317     .driver = {
0318         .name = "cm3605",
0319         .of_match_table = cm3605_of_match,
0320         .pm = pm_sleep_ptr(&cm3605_dev_pm_ops),
0321     },
0322     .probe = cm3605_probe,
0323     .remove = cm3605_remove,
0324 };
0325 module_platform_driver(cm3605_driver);
0326 
0327 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
0328 MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver");
0329 MODULE_LICENSE("GPL");