| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * These are the two Sharp GP2AP002 variants supported by this driver:
- * GP2AP002A00F Ambient Light and Proximity Sensor
- * GP2AP002S00F Proximity Sensor
- *
- * Copyright (C) 2020 Linaro Ltd.
- * Author: Linus Walleij <linus.walleij@linaro.org>
- *
- * Based partly on the code in Sony Ericssons GP2AP00200F driver by
- * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c
- * Based partly on a Samsung misc driver submitted by
- * Donggeun Kim & Minkyu Kang in 2011:
- * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/
- * Based partly on a submission by
- * Jonathan Bakker and Paweł Chmiel in january 2019:
- * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/
- * Based partly on code from the Samsung GT-S7710 by <mjchen@sta.samsung.com>
- * Based partly on the code in LG Electronics GP2AP00200F driver by
- * Kenobi Lee <sungyoung.lee@lge.com> and EunYoung Cho <ey.cho@lge.com>
- */
- #include <linux/module.h>
- #include <linux/i2c.h>
- #include <linux/regmap.h>
- #include <linux/iio/iio.h>
- #include <linux/iio/sysfs.h>
- #include <linux/iio/events.h>
- #include <linux/iio/consumer.h> /* To get our ADC channel */
- #include <linux/iio/types.h> /* To deal with our ADC channel */
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/regulator/consumer.h>
- #include <linux/pm_runtime.h>
- #include <linux/interrupt.h>
- #include <linux/bits.h>
- #include <linux/math64.h>
- #include <linux/pm.h>
- #define GP2AP002_PROX_CHANNEL 0
- #define GP2AP002_ALS_CHANNEL 1
- /* ------------------------------------------------------------------------ */
- /* ADDRESS SYMBOL DATA Init R/W */
- /* D7 D6 D5 D4 D3 D2 D1 D0 */
- /* ------------------------------------------------------------------------ */
- /* 0 PROX X X X X X X X VO H'00 R */
- /* 1 GAIN X X X X LED0 X X X H'00 W */
- /* 2 HYS HYSD HYSC1 HYSC0 X HYSF3 HYSF2 HYSF1 HYSF0 H'00 W */
- /* 3 CYCLE X X CYCL2 CYCL1 CYCL0 OSC2 X X H'00 W */
- /* 4 OPMOD X X X ASD X X VCON SSD H'00 W */
- /* 6 CON X X X OCON1 OCON0 X X X H'00 W */
- /* ------------------------------------------------------------------------ */
- /* VO :Proximity sensing result(0: no detection, 1: detection) */
- /* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/
- /* HYSD/HYSF :Adjusts the receiver sensitivity */
- /* OSC :Select switch internal clocl frequency hoppling(0:effective) */
- /* CYCL :Determine the detection cycle(typically 8ms, up to 128x) */
- /* SSD :Software Shutdown function(0:shutdown, 1:operating) */
- /* VCON :VOUT output method control(0:normal, 1:interrupt) */
- /* ASD :Select switch for analog sleep function(0:ineffective, 1:effective)*/
- /* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable) */
- #define GP2AP002_PROX 0x00
- #define GP2AP002_GAIN 0x01
- #define GP2AP002_HYS 0x02
- #define GP2AP002_CYCLE 0x03
- #define GP2AP002_OPMOD 0x04
- #define GP2AP002_CON 0x06
- #define GP2AP002_PROX_VO_DETECT BIT(0)
- /* Setting this bit to 0 means 2x higher LED resistance */
- #define GP2AP002_GAIN_LED_NORMAL BIT(3)
- /*
- * These bits adjusts the proximity sensitivity, determining characteristics
- * of the detection distance and its hysteresis.
- */
- #define GP2AP002_HYS_HYSD_SHIFT 7
- #define GP2AP002_HYS_HYSD_MASK BIT(7)
- #define GP2AP002_HYS_HYSC_SHIFT 5
- #define GP2AP002_HYS_HYSC_MASK GENMASK(6, 5)
- #define GP2AP002_HYS_HYSF_SHIFT 0
- #define GP2AP002_HYS_HYSF_MASK GENMASK(3, 0)
- #define GP2AP002_HYS_MASK (GP2AP002_HYS_HYSD_MASK | \
- GP2AP002_HYS_HYSC_MASK | \
- GP2AP002_HYS_HYSF_MASK)
- /*
- * These values determine the detection cycle response time
- * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms,
- * 5: 256ms, 6: 512ms, 7: 1024ms
- */
- #define GP2AP002_CYCLE_CYCL_SHIFT 3
- #define GP2AP002_CYCLE_CYCL_MASK GENMASK(5, 3)
- /*
- * Select switch for internal clock frequency hopping
- * 0: effective,
- * 1: ineffective
- */
- #define GP2AP002_CYCLE_OSC_EFFECTIVE 0
- #define GP2AP002_CYCLE_OSC_INEFFECTIVE BIT(2)
- #define GP2AP002_CYCLE_OSC_MASK BIT(2)
- /* Analog sleep effective */
- #define GP2AP002_OPMOD_ASD BIT(4)
- /* Enable chip */
- #define GP2AP002_OPMOD_SSD_OPERATING BIT(0)
- /* IRQ mode */
- #define GP2AP002_OPMOD_VCON_IRQ BIT(1)
- #define GP2AP002_OPMOD_MASK (BIT(0) | BIT(1) | BIT(4))
- /*
- * Select switch for enabling/disabling Vout pin
- * 0: enable
- * 2: force to go Low
- * 3: force to go High
- */
- #define GP2AP002_CON_OCON_SHIFT 3
- #define GP2AP002_CON_OCON_ENABLE (0x0 << GP2AP002_CON_OCON_SHIFT)
- #define GP2AP002_CON_OCON_LOW (0x2 << GP2AP002_CON_OCON_SHIFT)
- #define GP2AP002_CON_OCON_HIGH (0x3 << GP2AP002_CON_OCON_SHIFT)
- #define GP2AP002_CON_OCON_MASK (0x3 << GP2AP002_CON_OCON_SHIFT)
- /**
- * struct gp2ap002 - GP2AP002 state
- * @map: regmap pointer for the i2c regmap
- * @dev: pointer to parent device
- * @vdd: regulator controlling VDD
- * @vio: regulator controlling VIO
- * @alsout: IIO ADC channel to convert the ALSOUT signal
- * @hys_far: hysteresis control from device tree
- * @hys_close: hysteresis control from device tree
- * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip
- * @irq: the IRQ line used by this device
- * @enabled: we cannot read the status of the hardware so we need to
- * keep track of whether the event is enabled using this state variable
- */
- struct gp2ap002 {
- struct regmap *map;
- struct device *dev;
- struct regulator *vdd;
- struct regulator *vio;
- struct iio_channel *alsout;
- u8 hys_far;
- u8 hys_close;
- bool is_gp2ap002s00f;
- int irq;
- bool enabled;
- };
- static irqreturn_t gp2ap002_prox_irq(int irq, void *d)
- {
- struct iio_dev *indio_dev = d;
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- u64 ev;
- int val;
- int ret;
- if (!gp2ap002->enabled)
- goto err_retrig;
- ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val);
- if (ret) {
- dev_err(gp2ap002->dev, "error reading proximity\n");
- goto err_retrig;
- }
- if (val & GP2AP002_PROX_VO_DETECT) {
- /* Close */
- dev_dbg(gp2ap002->dev, "close\n");
- ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
- gp2ap002->hys_far);
- if (ret)
- dev_err(gp2ap002->dev,
- "error setting up proximity hysteresis\n");
- ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
- IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
- } else {
- /* Far */
- dev_dbg(gp2ap002->dev, "far\n");
- ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
- gp2ap002->hys_close);
- if (ret)
- dev_err(gp2ap002->dev,
- "error setting up proximity hysteresis\n");
- ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
- IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING);
- }
- iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
- /*
- * After changing hysteresis, we need to wait for one detection
- * cycle to see if anything changed, or we will just trigger the
- * previous interrupt again. A detection cycle depends on the CYCLE
- * register, we are hard-coding ~8 ms in probe() so wait some more
- * than this, 20-30 ms.
- */
- usleep_range(20000, 30000);
- err_retrig:
- ret = regmap_write(gp2ap002->map, GP2AP002_CON,
- GP2AP002_CON_OCON_ENABLE);
- if (ret)
- dev_err(gp2ap002->dev, "error setting up VOUT control\n");
- return IRQ_HANDLED;
- }
- /*
- * This array maps current and lux.
- *
- * Ambient light sensing range is 3 to 55000 lux.
- *
- * This mapping is based on the following formula.
- * illuminance = 10 ^ (current[mA] / 10)
- *
- * When the ADC measures 0, return 0 lux.
- */
- static const u16 gp2ap002_illuminance_table[] = {
- 0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79,
- 100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585,
- 1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953,
- 25119, 31623, 39811, 50119,
- };
- static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
- {
- int ret, res;
- u16 lux;
- ret = iio_read_channel_processed(gp2ap002->alsout, &res);
- if (ret < 0)
- return ret;
- dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
- /* ensure we don't under/overflow */
- res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1);
- lux = gp2ap002_illuminance_table[res];
- return (int)lux;
- }
- static int gp2ap002_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- int ret;
- pm_runtime_get_sync(gp2ap002->dev);
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- switch (chan->type) {
- case IIO_LIGHT:
- ret = gp2ap002_get_lux(gp2ap002);
- if (ret < 0)
- return ret;
- *val = ret;
- ret = IIO_VAL_INT;
- goto out;
- default:
- ret = -EINVAL;
- goto out;
- }
- default:
- ret = -EINVAL;
- }
- out:
- pm_runtime_mark_last_busy(gp2ap002->dev);
- pm_runtime_put_autosuspend(gp2ap002->dev);
- return ret;
- }
- static int gp2ap002_init(struct gp2ap002 *gp2ap002)
- {
- int ret;
- /* Set up the IR LED resistance */
- ret = regmap_write(gp2ap002->map, GP2AP002_GAIN,
- GP2AP002_GAIN_LED_NORMAL);
- if (ret) {
- dev_err(gp2ap002->dev, "error setting up LED gain\n");
- return ret;
- }
- ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far);
- if (ret) {
- dev_err(gp2ap002->dev,
- "error setting up proximity hysteresis\n");
- return ret;
- }
- /* Disable internal frequency hopping */
- ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE,
- GP2AP002_CYCLE_OSC_INEFFECTIVE);
- if (ret) {
- dev_err(gp2ap002->dev,
- "error setting up internal frequency hopping\n");
- return ret;
- }
- /* Enable chip and IRQ, disable analog sleep */
- ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD,
- GP2AP002_OPMOD_SSD_OPERATING |
- GP2AP002_OPMOD_VCON_IRQ);
- if (ret) {
- dev_err(gp2ap002->dev, "error setting up operation mode\n");
- return ret;
- }
- /* Interrupt on VOUT enabled */
- ret = regmap_write(gp2ap002->map, GP2AP002_CON,
- GP2AP002_CON_OCON_ENABLE);
- if (ret)
- dev_err(gp2ap002->dev, "error setting up VOUT control\n");
- return ret;
- }
- static int gp2ap002_read_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir)
- {
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- /*
- * We just keep track of this internally, as it is not possible to
- * query the hardware.
- */
- return gp2ap002->enabled;
- }
- static int gp2ap002_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir,
- int state)
- {
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- if (state) {
- /*
- * This will bring the regulators up (unless they are on
- * already) and reintialize the sensor by using runtime_pm
- * callbacks.
- */
- pm_runtime_get_sync(gp2ap002->dev);
- gp2ap002->enabled = true;
- } else {
- pm_runtime_mark_last_busy(gp2ap002->dev);
- pm_runtime_put_autosuspend(gp2ap002->dev);
- gp2ap002->enabled = false;
- }
- return 0;
- }
- static const struct iio_info gp2ap002_info = {
- .read_raw = gp2ap002_read_raw,
- .read_event_config = gp2ap002_read_event_config,
- .write_event_config = gp2ap002_write_event_config,
- };
- static const struct iio_event_spec gp2ap002_events[] = {
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_separate = BIT(IIO_EV_INFO_ENABLE),
- },
- };
- static const struct iio_chan_spec gp2ap002_channels[] = {
- {
- .type = IIO_PROXIMITY,
- .event_spec = gp2ap002_events,
- .num_event_specs = ARRAY_SIZE(gp2ap002_events),
- },
- {
- .type = IIO_LIGHT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .channel = GP2AP002_ALS_CHANNEL,
- },
- };
- /*
- * We need a special regmap because this hardware expects to
- * write single bytes to registers but read a 16bit word on some
- * variants and discard the lower 8 bits so combine
- * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data()
- * selectively like this.
- */
- static int gp2ap002_regmap_i2c_read(void *context, unsigned int reg,
- unsigned int *val)
- {
- struct device *dev = context;
- struct i2c_client *i2c = to_i2c_client(dev);
- int ret;
- ret = i2c_smbus_read_word_data(i2c, reg);
- if (ret < 0)
- return ret;
- *val = (ret >> 8) & 0xFF;
- return 0;
- }
- static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
- unsigned int val)
- {
- struct device *dev = context;
- struct i2c_client *i2c = to_i2c_client(dev);
- return i2c_smbus_write_byte_data(i2c, reg, val);
- }
- static const struct regmap_bus gp2ap002_regmap_bus = {
- .reg_read = gp2ap002_regmap_i2c_read,
- .reg_write = gp2ap002_regmap_i2c_write,
- };
- static int gp2ap002_probe(struct i2c_client *client)
- {
- struct gp2ap002 *gp2ap002;
- struct iio_dev *indio_dev;
- struct device *dev = &client->dev;
- enum iio_chan_type ch_type;
- static const struct regmap_config config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = GP2AP002_CON,
- };
- struct regmap *regmap;
- int num_chan;
- const char *compat;
- u8 val;
- int ret;
- indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
- if (!indio_dev)
- return -ENOMEM;
- i2c_set_clientdata(client, indio_dev);
- gp2ap002 = iio_priv(indio_dev);
- gp2ap002->dev = dev;
- /*
- * Check the device compatible like this makes it possible to use
- * ACPI PRP0001 for registering the sensor using device tree
- * properties.
- */
- ret = device_property_read_string(dev, "compatible", &compat);
- if (ret) {
- dev_err(dev, "cannot check compatible\n");
- return ret;
- }
- gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");
- regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
- if (IS_ERR(regmap)) {
- dev_err(dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
- gp2ap002->map = regmap;
- /*
- * The hysteresis settings are coded into the device tree as values
- * to be written into the hysteresis register. The datasheet defines
- * modes "A", "B1" and "B2" with fixed values to be use but vendor
- * code trees for actual devices are tweaking these values and refer to
- * modes named things like "B1.5". To be able to support any devices,
- * we allow passing an arbitrary hysteresis setting for "near" and
- * "far".
- */
- /* Check the device tree for the IR LED hysteresis */
- ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
- &val);
- if (ret) {
- dev_err(dev, "failed to obtain proximity far setting\n");
- return ret;
- }
- dev_dbg(dev, "proximity far setting %02x\n", val);
- gp2ap002->hys_far = val;
- ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
- &val);
- if (ret) {
- dev_err(dev, "failed to obtain proximity close setting\n");
- return ret;
- }
- dev_dbg(dev, "proximity close setting %02x\n", val);
- gp2ap002->hys_close = val;
- /* The GP2AP002A00F has a light sensor too */
- if (!gp2ap002->is_gp2ap002s00f) {
- gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
- if (IS_ERR(gp2ap002->alsout)) {
- ret = PTR_ERR(gp2ap002->alsout);
- ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret;
- return dev_err_probe(dev, ret, "failed to get ALSOUT ADC channel\n");
- }
- ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
- if (ret < 0)
- return ret;
- if (ch_type != IIO_CURRENT) {
- dev_err(dev,
- "wrong type of IIO channel specified for ALSOUT\n");
- return -EINVAL;
- }
- }
- gp2ap002->vdd = devm_regulator_get(dev, "vdd");
- if (IS_ERR(gp2ap002->vdd))
- return dev_err_probe(dev, PTR_ERR(gp2ap002->vdd),
- "failed to get VDD regulator\n");
- gp2ap002->vio = devm_regulator_get(dev, "vio");
- if (IS_ERR(gp2ap002->vio))
- return dev_err_probe(dev, PTR_ERR(gp2ap002->vio),
- "failed to get VIO regulator\n");
- /* Operating voltage 2.4V .. 3.6V according to datasheet */
- ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
- if (ret) {
- dev_err(dev, "failed to sett VDD voltage\n");
- return ret;
- }
- /* VIO should be between 1.65V and VDD */
- ret = regulator_get_voltage(gp2ap002->vdd);
- if (ret < 0) {
- dev_err(dev, "failed to get VDD voltage\n");
- return ret;
- }
- ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
- if (ret) {
- dev_err(dev, "failed to set VIO voltage\n");
- return ret;
- }
- ret = regulator_enable(gp2ap002->vdd);
- if (ret) {
- dev_err(dev, "failed to enable VDD regulator\n");
- return ret;
- }
- ret = regulator_enable(gp2ap002->vio);
- if (ret) {
- dev_err(dev, "failed to enable VIO regulator\n");
- goto out_disable_vdd;
- }
- msleep(20);
- /*
- * Initialize the device and signal to runtime PM that now we are
- * definitely up and using power.
- */
- ret = gp2ap002_init(gp2ap002);
- if (ret) {
- dev_err(dev, "initialization failed\n");
- goto out_disable_vio;
- }
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- gp2ap002->enabled = false;
- ret = devm_request_threaded_irq(dev, client->irq, NULL,
- gp2ap002_prox_irq, IRQF_ONESHOT,
- "gp2ap002", indio_dev);
- if (ret) {
- dev_err(dev, "unable to request IRQ\n");
- goto out_put_pm;
- }
- gp2ap002->irq = client->irq;
- /*
- * As the device takes 20 ms + regulator delay to come up with a fresh
- * measurement after power-on, do not shut it down unnecessarily.
- * Set autosuspend to a one second.
- */
- pm_runtime_set_autosuspend_delay(dev, 1000);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_put(dev);
- indio_dev->info = &gp2ap002_info;
- indio_dev->name = "gp2ap002";
- indio_dev->channels = gp2ap002_channels;
- /* Skip light channel for the proximity-only sensor */
- num_chan = ARRAY_SIZE(gp2ap002_channels);
- if (gp2ap002->is_gp2ap002s00f)
- num_chan--;
- indio_dev->num_channels = num_chan;
- indio_dev->modes = INDIO_DIRECT_MODE;
- ret = iio_device_register(indio_dev);
- if (ret)
- goto out_disable_pm;
- dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");
- return 0;
- out_put_pm:
- pm_runtime_put_noidle(dev);
- out_disable_pm:
- pm_runtime_disable(dev);
- out_disable_vio:
- regulator_disable(gp2ap002->vio);
- out_disable_vdd:
- regulator_disable(gp2ap002->vdd);
- return ret;
- }
- static void gp2ap002_remove(struct i2c_client *client)
- {
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- struct device *dev = &client->dev;
- pm_runtime_get_sync(dev);
- pm_runtime_put_noidle(dev);
- pm_runtime_disable(dev);
- iio_device_unregister(indio_dev);
- regulator_disable(gp2ap002->vio);
- regulator_disable(gp2ap002->vdd);
- }
- static int gp2ap002_runtime_suspend(struct device *dev)
- {
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- int ret;
- /* Deactivate the IRQ */
- disable_irq(gp2ap002->irq);
- /* Disable chip and IRQ, everything off */
- ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
- if (ret) {
- dev_err(gp2ap002->dev, "error setting up operation mode\n");
- return ret;
- }
- /*
- * As these regulators may be shared, at least we are now in
- * sleep even if the regulators aren't really turned off.
- */
- regulator_disable(gp2ap002->vio);
- regulator_disable(gp2ap002->vdd);
- return 0;
- }
- static int gp2ap002_runtime_resume(struct device *dev)
- {
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
- int ret;
- ret = regulator_enable(gp2ap002->vdd);
- if (ret) {
- dev_err(dev, "failed to enable VDD regulator in resume path\n");
- return ret;
- }
- ret = regulator_enable(gp2ap002->vio);
- if (ret) {
- dev_err(dev, "failed to enable VIO regulator in resume path\n");
- return ret;
- }
- msleep(20);
- ret = gp2ap002_init(gp2ap002);
- if (ret) {
- dev_err(dev, "re-initialization failed\n");
- return ret;
- }
- /* Re-activate the IRQ */
- enable_irq(gp2ap002->irq);
- return 0;
- }
- static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend,
- gp2ap002_runtime_resume, NULL);
- static const struct i2c_device_id gp2ap002_id_table[] = {
- { "gp2ap002" },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table);
- static const struct of_device_id gp2ap002_of_match[] = {
- { .compatible = "sharp,gp2ap002a00f" },
- { .compatible = "sharp,gp2ap002s00f" },
- { },
- };
- MODULE_DEVICE_TABLE(of, gp2ap002_of_match);
- static struct i2c_driver gp2ap002_driver = {
- .driver = {
- .name = "gp2ap002",
- .of_match_table = gp2ap002_of_match,
- .pm = pm_ptr(&gp2ap002_dev_pm_ops),
- },
- .probe = gp2ap002_probe,
- .remove = gp2ap002_remove,
- .id_table = gp2ap002_id_table,
- };
- module_i2c_driver(gp2ap002_driver);
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
- MODULE_LICENSE("GPL v2");
|