root/drivers/iio/light/cm3605.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cm3605_prox_irq
  2. cm3605_get_lux
  3. cm3605_read_raw
  4. cm3605_probe
  5. cm3605_remove
  6. cm3605_pm_suspend
  7. cm3605_pm_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * CM3605 Ambient Light and Proximity Sensor
   4  *
   5  * Copyright (C) 2016 Linaro Ltd.
   6  * Author: Linus Walleij <linus.walleij@linaro.org>
   7  *
   8  * This hardware was found in the very first Nexus One handset from Google/HTC
   9  * and an early endavour into mobile light and proximity sensors.
  10  */
  11 
  12 #include <linux/module.h>
  13 #include <linux/iio/iio.h>
  14 #include <linux/iio/sysfs.h>
  15 #include <linux/iio/events.h>
  16 #include <linux/iio/consumer.h> /* To get our ADC channel */
  17 #include <linux/iio/types.h> /* To deal with our ADC channel */
  18 #include <linux/init.h>
  19 #include <linux/leds.h>
  20 #include <linux/platform_device.h>
  21 #include <linux/of.h>
  22 #include <linux/regulator/consumer.h>
  23 #include <linux/gpio/consumer.h>
  24 #include <linux/interrupt.h>
  25 #include <linux/math64.h>
  26 #include <linux/pm.h>
  27 
  28 #define CM3605_PROX_CHANNEL 0
  29 #define CM3605_ALS_CHANNEL 1
  30 #define CM3605_AOUT_TYP_MAX_MV 1550
  31 /* It should not go above 1.650V according to the data sheet */
  32 #define CM3605_AOUT_MAX_MV 1650
  33 
  34 /**
  35  * struct cm3605 - CM3605 state
  36  * @dev: pointer to parent device
  37  * @vdd: regulator controlling VDD
  38  * @aset: sleep enable GPIO, high = sleep
  39  * @aout: IIO ADC channel to convert the AOUT signal
  40  * @als_max: maximum LUX detection (depends on RSET)
  41  * @dir: proximity direction: start as FALLING
  42  * @led: trigger for the infrared LED used by the proximity sensor
  43  */
  44 struct cm3605 {
  45         struct device *dev;
  46         struct regulator *vdd;
  47         struct gpio_desc *aset;
  48         struct iio_channel *aout;
  49         s32 als_max;
  50         enum iio_event_direction dir;
  51         struct led_trigger *led;
  52 };
  53 
  54 static irqreturn_t cm3605_prox_irq(int irq, void *d)
  55 {
  56         struct iio_dev *indio_dev = d;
  57         struct cm3605 *cm3605 = iio_priv(indio_dev);
  58         u64 ev;
  59 
  60         ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, CM3605_PROX_CHANNEL,
  61                                   IIO_EV_TYPE_THRESH, cm3605->dir);
  62         iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
  63 
  64         /* Invert the edge for each event */
  65         if (cm3605->dir == IIO_EV_DIR_RISING)
  66                 cm3605->dir = IIO_EV_DIR_FALLING;
  67         else
  68                 cm3605->dir = IIO_EV_DIR_RISING;
  69 
  70         return IRQ_HANDLED;
  71 }
  72 
  73 static int cm3605_get_lux(struct cm3605 *cm3605)
  74 {
  75         int ret, res;
  76         s64 lux;
  77 
  78         ret = iio_read_channel_processed(cm3605->aout, &res);
  79         if (ret < 0)
  80                 return ret;
  81 
  82         dev_dbg(cm3605->dev, "read %d mV from ADC\n", res);
  83 
  84         /*
  85          * AOUT has an offset of ~30mV then linear at dark
  86          * then goes from 2.54 up to 650 LUX yielding 1.55V
  87          * (1550 mV) so scale the returned value to this interval
  88          * using simple linear interpolation.
  89          */
  90         if (res < 30)
  91                 return 0;
  92         if (res > CM3605_AOUT_MAX_MV)
  93                 dev_err(cm3605->dev, "device out of range\n");
  94 
  95         /* Remove bias */
  96         lux = res - 30;
  97 
  98         /* Linear interpolation between 0 and ALS typ max */
  99         lux *= cm3605->als_max;
 100         lux = div64_s64(lux, CM3605_AOUT_TYP_MAX_MV);
 101 
 102         return lux;
 103 }
 104 
 105 static int cm3605_read_raw(struct iio_dev *indio_dev,
 106                            struct iio_chan_spec const *chan,
 107                            int *val, int *val2, long mask)
 108 {
 109         struct cm3605 *cm3605 = iio_priv(indio_dev);
 110         int ret;
 111 
 112         switch (mask) {
 113         case IIO_CHAN_INFO_RAW:
 114                 switch (chan->type) {
 115                 case IIO_LIGHT:
 116                         ret = cm3605_get_lux(cm3605);
 117                         if (ret < 0)
 118                                 return ret;
 119                         *val = ret;
 120                         return IIO_VAL_INT;
 121                 default:
 122                         return -EINVAL;
 123                 }
 124         default:
 125                 return -EINVAL;
 126         }
 127 }
 128 
 129 static const struct iio_info cm3605_info = {
 130         .read_raw = cm3605_read_raw,
 131 };
 132 
 133 static const struct iio_event_spec cm3605_events[] = {
 134         {
 135                 .type = IIO_EV_TYPE_THRESH,
 136                 .dir = IIO_EV_DIR_EITHER,
 137                 .mask_separate = BIT(IIO_EV_INFO_ENABLE),
 138         },
 139 };
 140 
 141 static const struct iio_chan_spec cm3605_channels[] = {
 142         {
 143                 .type = IIO_PROXIMITY,
 144                 .event_spec = cm3605_events,
 145                 .num_event_specs = ARRAY_SIZE(cm3605_events),
 146         },
 147         {
 148                 .type = IIO_LIGHT,
 149                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 150                 .channel = CM3605_ALS_CHANNEL,
 151         },
 152 };
 153 
 154 static int cm3605_probe(struct platform_device *pdev)
 155 {
 156         struct cm3605 *cm3605;
 157         struct iio_dev *indio_dev;
 158         struct device *dev = &pdev->dev;
 159         struct device_node *np = dev->of_node;
 160         enum iio_chan_type ch_type;
 161         u32 rset;
 162         int ret;
 163 
 164         indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605));
 165         if (!indio_dev)
 166                 return -ENOMEM;
 167         platform_set_drvdata(pdev, indio_dev);
 168 
 169         cm3605 = iio_priv(indio_dev);
 170         cm3605->dev = dev;
 171         cm3605->dir = IIO_EV_DIR_FALLING;
 172 
 173         ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset);
 174         if (ret) {
 175                 dev_info(dev, "no RSET specified, assuming 100K\n");
 176                 rset = 100000;
 177         }
 178         switch (rset) {
 179         case 50000:
 180                 cm3605->als_max = 650;
 181                 break;
 182         case 100000:
 183                 cm3605->als_max = 300;
 184                 break;
 185         case 300000:
 186                 cm3605->als_max = 100;
 187                 break;
 188         case 600000:
 189                 cm3605->als_max = 50;
 190                 break;
 191         default:
 192                 dev_info(dev, "non-standard resistance\n");
 193                 return -EINVAL;
 194         }
 195 
 196         cm3605->aout = devm_iio_channel_get(dev, "aout");
 197         if (IS_ERR(cm3605->aout)) {
 198                 if (PTR_ERR(cm3605->aout) == -ENODEV) {
 199                         dev_err(dev, "no ADC, deferring...\n");
 200                         return -EPROBE_DEFER;
 201                 }
 202                 dev_err(dev, "failed to get AOUT ADC channel\n");
 203                 return PTR_ERR(cm3605->aout);
 204         }
 205         ret = iio_get_channel_type(cm3605->aout, &ch_type);
 206         if (ret < 0)
 207                 return ret;
 208         if (ch_type != IIO_VOLTAGE) {
 209                 dev_err(dev, "wrong type of IIO channel specified for AOUT\n");
 210                 return -EINVAL;
 211         }
 212 
 213         cm3605->vdd = devm_regulator_get(dev, "vdd");
 214         if (IS_ERR(cm3605->vdd)) {
 215                 dev_err(dev, "failed to get VDD regulator\n");
 216                 return PTR_ERR(cm3605->vdd);
 217         }
 218         ret = regulator_enable(cm3605->vdd);
 219         if (ret) {
 220                 dev_err(dev, "failed to enable VDD regulator\n");
 221                 return ret;
 222         }
 223 
 224         cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH);
 225         if (IS_ERR(cm3605->aset)) {
 226                 dev_err(dev, "no ASET GPIO\n");
 227                 ret = PTR_ERR(cm3605->aset);
 228                 goto out_disable_vdd;
 229         }
 230 
 231         ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
 232                         cm3605_prox_irq, NULL, 0, "cm3605", indio_dev);
 233         if (ret) {
 234                 dev_err(dev, "unable to request IRQ\n");
 235                 goto out_disable_aset;
 236         }
 237 
 238         /* Just name the trigger the same as the driver */
 239         led_trigger_register_simple("cm3605", &cm3605->led);
 240         led_trigger_event(cm3605->led, LED_FULL);
 241 
 242         indio_dev->dev.parent = dev;
 243         indio_dev->info = &cm3605_info;
 244         indio_dev->name = "cm3605";
 245         indio_dev->channels = cm3605_channels;
 246         indio_dev->num_channels = ARRAY_SIZE(cm3605_channels);
 247         indio_dev->modes = INDIO_DIRECT_MODE;
 248 
 249         ret = iio_device_register(indio_dev);
 250         if (ret)
 251                 goto out_remove_trigger;
 252         dev_info(dev, "Capella Microsystems CM3605 enabled range 0..%d LUX\n",
 253                  cm3605->als_max);
 254 
 255         return 0;
 256 
 257 out_remove_trigger:
 258         led_trigger_event(cm3605->led, LED_OFF);
 259         led_trigger_unregister_simple(cm3605->led);
 260 out_disable_aset:
 261         gpiod_set_value_cansleep(cm3605->aset, 0);
 262 out_disable_vdd:
 263         regulator_disable(cm3605->vdd);
 264         return ret;
 265 }
 266 
 267 static int cm3605_remove(struct platform_device *pdev)
 268 {
 269         struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 270         struct cm3605 *cm3605 = iio_priv(indio_dev);
 271 
 272         led_trigger_event(cm3605->led, LED_OFF);
 273         led_trigger_unregister_simple(cm3605->led);
 274         gpiod_set_value_cansleep(cm3605->aset, 0);
 275         iio_device_unregister(indio_dev);
 276         regulator_disable(cm3605->vdd);
 277 
 278         return 0;
 279 }
 280 
 281 static int __maybe_unused cm3605_pm_suspend(struct device *dev)
 282 {
 283         struct iio_dev *indio_dev = dev_get_drvdata(dev);
 284         struct cm3605 *cm3605 = iio_priv(indio_dev);
 285 
 286         led_trigger_event(cm3605->led, LED_OFF);
 287         regulator_disable(cm3605->vdd);
 288 
 289         return 0;
 290 }
 291 
 292 static int __maybe_unused cm3605_pm_resume(struct device *dev)
 293 {
 294         struct iio_dev *indio_dev = dev_get_drvdata(dev);
 295         struct cm3605 *cm3605 = iio_priv(indio_dev);
 296         int ret;
 297 
 298         ret = regulator_enable(cm3605->vdd);
 299         if (ret)
 300                 dev_err(dev, "failed to enable regulator in resume path\n");
 301         led_trigger_event(cm3605->led, LED_FULL);
 302 
 303         return 0;
 304 }
 305 
 306 static const struct dev_pm_ops cm3605_dev_pm_ops = {
 307         SET_SYSTEM_SLEEP_PM_OPS(cm3605_pm_suspend,
 308                                 cm3605_pm_resume)
 309 };
 310 
 311 static const struct of_device_id cm3605_of_match[] = {
 312         {.compatible = "capella,cm3605"},
 313         { },
 314 };
 315 MODULE_DEVICE_TABLE(of, cm3605_of_match);
 316 
 317 static struct platform_driver cm3605_driver = {
 318         .driver = {
 319                 .name = "cm3605",
 320                 .of_match_table = cm3605_of_match,
 321                 .pm = &cm3605_dev_pm_ops,
 322         },
 323         .probe = cm3605_probe,
 324         .remove = cm3605_remove,
 325 };
 326 module_platform_driver(cm3605_driver);
 327 
 328 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
 329 MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver");
 330 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */