162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * These are the two Sharp GP2AP002 variants supported by this driver:
462306a36Sopenharmony_ci * GP2AP002A00F Ambient Light and Proximity Sensor
562306a36Sopenharmony_ci * GP2AP002S00F Proximity Sensor
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2020 Linaro Ltd.
862306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Based partly on the code in Sony Ericssons GP2AP00200F driver by
1162306a36Sopenharmony_ci * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c
1262306a36Sopenharmony_ci * Based partly on a Samsung misc driver submitted by
1362306a36Sopenharmony_ci * Donggeun Kim & Minkyu Kang in 2011:
1462306a36Sopenharmony_ci * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/
1562306a36Sopenharmony_ci * Based partly on a submission by
1662306a36Sopenharmony_ci * Jonathan Bakker and Paweł Chmiel in january 2019:
1762306a36Sopenharmony_ci * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/
1862306a36Sopenharmony_ci * Based partly on code from the Samsung GT-S7710 by <mjchen@sta.samsung.com>
1962306a36Sopenharmony_ci * Based partly on the code in LG Electronics GP2AP00200F driver by
2062306a36Sopenharmony_ci * Kenobi Lee <sungyoung.lee@lge.com> and EunYoung Cho <ey.cho@lge.com>
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/i2c.h>
2462306a36Sopenharmony_ci#include <linux/regmap.h>
2562306a36Sopenharmony_ci#include <linux/iio/iio.h>
2662306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2762306a36Sopenharmony_ci#include <linux/iio/events.h>
2862306a36Sopenharmony_ci#include <linux/iio/consumer.h> /* To get our ADC channel */
2962306a36Sopenharmony_ci#include <linux/iio/types.h> /* To deal with our ADC channel */
3062306a36Sopenharmony_ci#include <linux/init.h>
3162306a36Sopenharmony_ci#include <linux/delay.h>
3262306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3462306a36Sopenharmony_ci#include <linux/interrupt.h>
3562306a36Sopenharmony_ci#include <linux/bits.h>
3662306a36Sopenharmony_ci#include <linux/math64.h>
3762306a36Sopenharmony_ci#include <linux/pm.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define GP2AP002_PROX_CHANNEL 0
4062306a36Sopenharmony_ci#define GP2AP002_ALS_CHANNEL 1
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* ------------------------------------------------------------------------ */
4362306a36Sopenharmony_ci/* ADDRESS SYMBOL             DATA                                 Init R/W */
4462306a36Sopenharmony_ci/*                   D7    D6    D5    D4    D3    D2    D1    D0           */
4562306a36Sopenharmony_ci/* ------------------------------------------------------------------------ */
4662306a36Sopenharmony_ci/*    0      PROX     X     X     X     X     X     X     X    VO  H'00   R */
4762306a36Sopenharmony_ci/*    1      GAIN     X     X     X     X  LED0     X     X     X  H'00   W */
4862306a36Sopenharmony_ci/*    2       HYS  HYSD HYSC1 HYSC0     X HYSF3 HYSF2 HYSF1 HYSF0  H'00   W */
4962306a36Sopenharmony_ci/*    3     CYCLE     X     X CYCL2 CYCL1 CYCL0  OSC2     X     X  H'00   W */
5062306a36Sopenharmony_ci/*    4     OPMOD     X     X     X   ASD     X     X  VCON   SSD  H'00   W */
5162306a36Sopenharmony_ci/*    6       CON     X     X     X OCON1 OCON0     X     X     X  H'00   W */
5262306a36Sopenharmony_ci/* ------------------------------------------------------------------------ */
5362306a36Sopenharmony_ci/* VO   :Proximity sensing result(0: no detection, 1: detection)            */
5462306a36Sopenharmony_ci/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/
5562306a36Sopenharmony_ci/* HYSD/HYSF :Adjusts the receiver sensitivity                              */
5662306a36Sopenharmony_ci/* OSC  :Select switch internal clocl frequency hoppling(0:effective)       */
5762306a36Sopenharmony_ci/* CYCL :Determine the detection cycle(typically 8ms, up to 128x)           */
5862306a36Sopenharmony_ci/* SSD  :Software Shutdown function(0:shutdown, 1:operating)                */
5962306a36Sopenharmony_ci/* VCON :VOUT output method control(0:normal, 1:interrupt)                  */
6062306a36Sopenharmony_ci/* ASD  :Select switch for analog sleep function(0:ineffective, 1:effective)*/
6162306a36Sopenharmony_ci/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable)  */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define GP2AP002_PROX				0x00
6462306a36Sopenharmony_ci#define GP2AP002_GAIN				0x01
6562306a36Sopenharmony_ci#define GP2AP002_HYS				0x02
6662306a36Sopenharmony_ci#define GP2AP002_CYCLE				0x03
6762306a36Sopenharmony_ci#define GP2AP002_OPMOD				0x04
6862306a36Sopenharmony_ci#define GP2AP002_CON				0x06
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define GP2AP002_PROX_VO_DETECT			BIT(0)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Setting this bit to 0 means 2x higher LED resistance */
7362306a36Sopenharmony_ci#define GP2AP002_GAIN_LED_NORMAL		BIT(3)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * These bits adjusts the proximity sensitivity, determining characteristics
7762306a36Sopenharmony_ci * of the detection distance and its hysteresis.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_ci#define GP2AP002_HYS_HYSD_SHIFT		7
8062306a36Sopenharmony_ci#define GP2AP002_HYS_HYSD_MASK		BIT(7)
8162306a36Sopenharmony_ci#define GP2AP002_HYS_HYSC_SHIFT		5
8262306a36Sopenharmony_ci#define GP2AP002_HYS_HYSC_MASK		GENMASK(6, 5)
8362306a36Sopenharmony_ci#define GP2AP002_HYS_HYSF_SHIFT		0
8462306a36Sopenharmony_ci#define GP2AP002_HYS_HYSF_MASK		GENMASK(3, 0)
8562306a36Sopenharmony_ci#define GP2AP002_HYS_MASK		(GP2AP002_HYS_HYSD_MASK | \
8662306a36Sopenharmony_ci					 GP2AP002_HYS_HYSC_MASK | \
8762306a36Sopenharmony_ci					 GP2AP002_HYS_HYSF_MASK)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * These values determine the detection cycle response time
9162306a36Sopenharmony_ci * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms,
9262306a36Sopenharmony_ci * 5: 256ms, 6: 512ms, 7: 1024ms
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci#define GP2AP002_CYCLE_CYCL_SHIFT	3
9562306a36Sopenharmony_ci#define GP2AP002_CYCLE_CYCL_MASK	GENMASK(5, 3)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Select switch for internal clock frequency hopping
9962306a36Sopenharmony_ci *	0: effective,
10062306a36Sopenharmony_ci *	1: ineffective
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci#define GP2AP002_CYCLE_OSC_EFFECTIVE	0
10362306a36Sopenharmony_ci#define GP2AP002_CYCLE_OSC_INEFFECTIVE	BIT(2)
10462306a36Sopenharmony_ci#define GP2AP002_CYCLE_OSC_MASK		BIT(2)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Analog sleep effective */
10762306a36Sopenharmony_ci#define GP2AP002_OPMOD_ASD		BIT(4)
10862306a36Sopenharmony_ci/* Enable chip */
10962306a36Sopenharmony_ci#define GP2AP002_OPMOD_SSD_OPERATING	BIT(0)
11062306a36Sopenharmony_ci/* IRQ mode */
11162306a36Sopenharmony_ci#define GP2AP002_OPMOD_VCON_IRQ		BIT(1)
11262306a36Sopenharmony_ci#define GP2AP002_OPMOD_MASK		(BIT(0) | BIT(1) | BIT(4))
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*
11562306a36Sopenharmony_ci * Select switch for enabling/disabling Vout pin
11662306a36Sopenharmony_ci * 0: enable
11762306a36Sopenharmony_ci * 2: force to go Low
11862306a36Sopenharmony_ci * 3: force to go High
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_ci#define GP2AP002_CON_OCON_SHIFT		3
12162306a36Sopenharmony_ci#define GP2AP002_CON_OCON_ENABLE	(0x0 << GP2AP002_CON_OCON_SHIFT)
12262306a36Sopenharmony_ci#define GP2AP002_CON_OCON_LOW		(0x2 << GP2AP002_CON_OCON_SHIFT)
12362306a36Sopenharmony_ci#define GP2AP002_CON_OCON_HIGH		(0x3 << GP2AP002_CON_OCON_SHIFT)
12462306a36Sopenharmony_ci#define GP2AP002_CON_OCON_MASK		(0x3 << GP2AP002_CON_OCON_SHIFT)
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * struct gp2ap002 - GP2AP002 state
12862306a36Sopenharmony_ci * @map: regmap pointer for the i2c regmap
12962306a36Sopenharmony_ci * @dev: pointer to parent device
13062306a36Sopenharmony_ci * @vdd: regulator controlling VDD
13162306a36Sopenharmony_ci * @vio: regulator controlling VIO
13262306a36Sopenharmony_ci * @alsout: IIO ADC channel to convert the ALSOUT signal
13362306a36Sopenharmony_ci * @hys_far: hysteresis control from device tree
13462306a36Sopenharmony_ci * @hys_close: hysteresis control from device tree
13562306a36Sopenharmony_ci * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip
13662306a36Sopenharmony_ci * @irq: the IRQ line used by this device
13762306a36Sopenharmony_ci * @enabled: we cannot read the status of the hardware so we need to
13862306a36Sopenharmony_ci * keep track of whether the event is enabled using this state variable
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistruct gp2ap002 {
14162306a36Sopenharmony_ci	struct regmap *map;
14262306a36Sopenharmony_ci	struct device *dev;
14362306a36Sopenharmony_ci	struct regulator *vdd;
14462306a36Sopenharmony_ci	struct regulator *vio;
14562306a36Sopenharmony_ci	struct iio_channel *alsout;
14662306a36Sopenharmony_ci	u8 hys_far;
14762306a36Sopenharmony_ci	u8 hys_close;
14862306a36Sopenharmony_ci	bool is_gp2ap002s00f;
14962306a36Sopenharmony_ci	int irq;
15062306a36Sopenharmony_ci	bool enabled;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic irqreturn_t gp2ap002_prox_irq(int irq, void *d)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct iio_dev *indio_dev = d;
15662306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
15762306a36Sopenharmony_ci	u64 ev;
15862306a36Sopenharmony_ci	int val;
15962306a36Sopenharmony_ci	int ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!gp2ap002->enabled)
16262306a36Sopenharmony_ci		goto err_retrig;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val);
16562306a36Sopenharmony_ci	if (ret) {
16662306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error reading proximity\n");
16762306a36Sopenharmony_ci		goto err_retrig;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (val & GP2AP002_PROX_VO_DETECT) {
17162306a36Sopenharmony_ci		/* Close */
17262306a36Sopenharmony_ci		dev_dbg(gp2ap002->dev, "close\n");
17362306a36Sopenharmony_ci		ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
17462306a36Sopenharmony_ci				   gp2ap002->hys_far);
17562306a36Sopenharmony_ci		if (ret)
17662306a36Sopenharmony_ci			dev_err(gp2ap002->dev,
17762306a36Sopenharmony_ci				"error setting up proximity hysteresis\n");
17862306a36Sopenharmony_ci		ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
17962306a36Sopenharmony_ci					IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
18062306a36Sopenharmony_ci	} else {
18162306a36Sopenharmony_ci		/* Far */
18262306a36Sopenharmony_ci		dev_dbg(gp2ap002->dev, "far\n");
18362306a36Sopenharmony_ci		ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
18462306a36Sopenharmony_ci				   gp2ap002->hys_close);
18562306a36Sopenharmony_ci		if (ret)
18662306a36Sopenharmony_ci			dev_err(gp2ap002->dev,
18762306a36Sopenharmony_ci				"error setting up proximity hysteresis\n");
18862306a36Sopenharmony_ci		ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
18962306a36Sopenharmony_ci					IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * After changing hysteresis, we need to wait for one detection
19562306a36Sopenharmony_ci	 * cycle to see if anything changed, or we will just trigger the
19662306a36Sopenharmony_ci	 * previous interrupt again. A detection cycle depends on the CYCLE
19762306a36Sopenharmony_ci	 * register, we are hard-coding ~8 ms in probe() so wait some more
19862306a36Sopenharmony_ci	 * than this, 20-30 ms.
19962306a36Sopenharmony_ci	 */
20062306a36Sopenharmony_ci	usleep_range(20000, 30000);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cierr_retrig:
20362306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CON,
20462306a36Sopenharmony_ci			   GP2AP002_CON_OCON_ENABLE);
20562306a36Sopenharmony_ci	if (ret)
20662306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up VOUT control\n");
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return IRQ_HANDLED;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/*
21262306a36Sopenharmony_ci * This array maps current and lux.
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * Ambient light sensing range is 3 to 55000 lux.
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci * This mapping is based on the following formula.
21762306a36Sopenharmony_ci * illuminance = 10 ^ (current[mA] / 10)
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * When the ADC measures 0, return 0 lux.
22062306a36Sopenharmony_ci */
22162306a36Sopenharmony_cistatic const u16 gp2ap002_illuminance_table[] = {
22262306a36Sopenharmony_ci	0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79,
22362306a36Sopenharmony_ci	100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585,
22462306a36Sopenharmony_ci	1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953,
22562306a36Sopenharmony_ci	25119, 31623, 39811, 50119,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	int ret, res;
23162306a36Sopenharmony_ci	u16 lux;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = iio_read_channel_processed(gp2ap002->alsout, &res);
23462306a36Sopenharmony_ci	if (ret < 0)
23562306a36Sopenharmony_ci		return ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* ensure we don't under/overflow */
24062306a36Sopenharmony_ci	res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1);
24162306a36Sopenharmony_ci	lux = gp2ap002_illuminance_table[res];
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return (int)lux;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int gp2ap002_read_raw(struct iio_dev *indio_dev,
24762306a36Sopenharmony_ci			   struct iio_chan_spec const *chan,
24862306a36Sopenharmony_ci			   int *val, int *val2, long mask)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
25162306a36Sopenharmony_ci	int ret;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	pm_runtime_get_sync(gp2ap002->dev);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	switch (mask) {
25662306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
25762306a36Sopenharmony_ci		switch (chan->type) {
25862306a36Sopenharmony_ci		case IIO_LIGHT:
25962306a36Sopenharmony_ci			ret = gp2ap002_get_lux(gp2ap002);
26062306a36Sopenharmony_ci			if (ret < 0)
26162306a36Sopenharmony_ci				return ret;
26262306a36Sopenharmony_ci			*val = ret;
26362306a36Sopenharmony_ci			ret = IIO_VAL_INT;
26462306a36Sopenharmony_ci			goto out;
26562306a36Sopenharmony_ci		default:
26662306a36Sopenharmony_ci			ret = -EINVAL;
26762306a36Sopenharmony_ci			goto out;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	default:
27062306a36Sopenharmony_ci		ret = -EINVAL;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciout:
27462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(gp2ap002->dev);
27562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(gp2ap002->dev);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return ret;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int gp2ap002_init(struct gp2ap002 *gp2ap002)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int ret;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Set up the IR LED resistance */
28562306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_GAIN,
28662306a36Sopenharmony_ci			   GP2AP002_GAIN_LED_NORMAL);
28762306a36Sopenharmony_ci	if (ret) {
28862306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up LED gain\n");
28962306a36Sopenharmony_ci		return ret;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far);
29262306a36Sopenharmony_ci	if (ret) {
29362306a36Sopenharmony_ci		dev_err(gp2ap002->dev,
29462306a36Sopenharmony_ci			"error setting up proximity hysteresis\n");
29562306a36Sopenharmony_ci		return ret;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Disable internal frequency hopping */
29962306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE,
30062306a36Sopenharmony_ci			   GP2AP002_CYCLE_OSC_INEFFECTIVE);
30162306a36Sopenharmony_ci	if (ret) {
30262306a36Sopenharmony_ci		dev_err(gp2ap002->dev,
30362306a36Sopenharmony_ci			"error setting up internal frequency hopping\n");
30462306a36Sopenharmony_ci		return ret;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Enable chip and IRQ, disable analog sleep */
30862306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD,
30962306a36Sopenharmony_ci			   GP2AP002_OPMOD_SSD_OPERATING |
31062306a36Sopenharmony_ci			   GP2AP002_OPMOD_VCON_IRQ);
31162306a36Sopenharmony_ci	if (ret) {
31262306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up operation mode\n");
31362306a36Sopenharmony_ci		return ret;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Interrupt on VOUT enabled */
31762306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CON,
31862306a36Sopenharmony_ci			   GP2AP002_CON_OCON_ENABLE);
31962306a36Sopenharmony_ci	if (ret)
32062306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up VOUT control\n");
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return ret;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int gp2ap002_read_event_config(struct iio_dev *indio_dev,
32662306a36Sopenharmony_ci				      const struct iio_chan_spec *chan,
32762306a36Sopenharmony_ci				      enum iio_event_type type,
32862306a36Sopenharmony_ci				      enum iio_event_direction dir)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/*
33362306a36Sopenharmony_ci	 * We just keep track of this internally, as it is not possible to
33462306a36Sopenharmony_ci	 * query the hardware.
33562306a36Sopenharmony_ci	 */
33662306a36Sopenharmony_ci	return gp2ap002->enabled;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int gp2ap002_write_event_config(struct iio_dev *indio_dev,
34062306a36Sopenharmony_ci				       const struct iio_chan_spec *chan,
34162306a36Sopenharmony_ci				       enum iio_event_type type,
34262306a36Sopenharmony_ci				       enum iio_event_direction dir,
34362306a36Sopenharmony_ci				       int state)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (state) {
34862306a36Sopenharmony_ci		/*
34962306a36Sopenharmony_ci		 * This will bring the regulators up (unless they are on
35062306a36Sopenharmony_ci		 * already) and reintialize the sensor by using runtime_pm
35162306a36Sopenharmony_ci		 * callbacks.
35262306a36Sopenharmony_ci		 */
35362306a36Sopenharmony_ci		pm_runtime_get_sync(gp2ap002->dev);
35462306a36Sopenharmony_ci		gp2ap002->enabled = true;
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		pm_runtime_mark_last_busy(gp2ap002->dev);
35762306a36Sopenharmony_ci		pm_runtime_put_autosuspend(gp2ap002->dev);
35862306a36Sopenharmony_ci		gp2ap002->enabled = false;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic const struct iio_info gp2ap002_info = {
36562306a36Sopenharmony_ci	.read_raw = gp2ap002_read_raw,
36662306a36Sopenharmony_ci	.read_event_config = gp2ap002_read_event_config,
36762306a36Sopenharmony_ci	.write_event_config = gp2ap002_write_event_config,
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic const struct iio_event_spec gp2ap002_events[] = {
37162306a36Sopenharmony_ci	{
37262306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
37362306a36Sopenharmony_ci		.dir = IIO_EV_DIR_EITHER,
37462306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
37562306a36Sopenharmony_ci	},
37662306a36Sopenharmony_ci};
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic const struct iio_chan_spec gp2ap002_channels[] = {
37962306a36Sopenharmony_ci	{
38062306a36Sopenharmony_ci		.type = IIO_PROXIMITY,
38162306a36Sopenharmony_ci		.event_spec = gp2ap002_events,
38262306a36Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(gp2ap002_events),
38362306a36Sopenharmony_ci	},
38462306a36Sopenharmony_ci	{
38562306a36Sopenharmony_ci		.type = IIO_LIGHT,
38662306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
38762306a36Sopenharmony_ci		.channel = GP2AP002_ALS_CHANNEL,
38862306a36Sopenharmony_ci	},
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/*
39262306a36Sopenharmony_ci * We need a special regmap because this hardware expects to
39362306a36Sopenharmony_ci * write single bytes to registers but read a 16bit word on some
39462306a36Sopenharmony_ci * variants and discard the lower 8 bits so combine
39562306a36Sopenharmony_ci * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data()
39662306a36Sopenharmony_ci * selectively like this.
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic int gp2ap002_regmap_i2c_read(void *context, unsigned int reg,
39962306a36Sopenharmony_ci				    unsigned int *val)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct device *dev = context;
40262306a36Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(i2c, reg);
40662306a36Sopenharmony_ci	if (ret < 0)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	*val = (ret >> 8) & 0xFF;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
41562306a36Sopenharmony_ci				     unsigned int val)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct device *dev = context;
41862306a36Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(i2c, reg, val);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic struct regmap_bus gp2ap002_regmap_bus = {
42462306a36Sopenharmony_ci	.reg_read = gp2ap002_regmap_i2c_read,
42562306a36Sopenharmony_ci	.reg_write = gp2ap002_regmap_i2c_write,
42662306a36Sopenharmony_ci};
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int gp2ap002_probe(struct i2c_client *client)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002;
43162306a36Sopenharmony_ci	struct iio_dev *indio_dev;
43262306a36Sopenharmony_ci	struct device *dev = &client->dev;
43362306a36Sopenharmony_ci	enum iio_chan_type ch_type;
43462306a36Sopenharmony_ci	static const struct regmap_config config = {
43562306a36Sopenharmony_ci		.reg_bits = 8,
43662306a36Sopenharmony_ci		.val_bits = 8,
43762306a36Sopenharmony_ci		.max_register = GP2AP002_CON,
43862306a36Sopenharmony_ci	};
43962306a36Sopenharmony_ci	struct regmap *regmap;
44062306a36Sopenharmony_ci	int num_chan;
44162306a36Sopenharmony_ci	const char *compat;
44262306a36Sopenharmony_ci	u8 val;
44362306a36Sopenharmony_ci	int ret;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
44662306a36Sopenharmony_ci	if (!indio_dev)
44762306a36Sopenharmony_ci		return -ENOMEM;
44862306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	gp2ap002 = iio_priv(indio_dev);
45162306a36Sopenharmony_ci	gp2ap002->dev = dev;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * Check the device compatible like this makes it possible to use
45562306a36Sopenharmony_ci	 * ACPI PRP0001 for registering the sensor using device tree
45662306a36Sopenharmony_ci	 * properties.
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	ret = device_property_read_string(dev, "compatible", &compat);
45962306a36Sopenharmony_ci	if (ret) {
46062306a36Sopenharmony_ci		dev_err(dev, "cannot check compatible\n");
46162306a36Sopenharmony_ci		return ret;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
46662306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
46762306a36Sopenharmony_ci		dev_err(dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap));
46862306a36Sopenharmony_ci		return PTR_ERR(regmap);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci	gp2ap002->map = regmap;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/*
47362306a36Sopenharmony_ci	 * The hysteresis settings are coded into the device tree as values
47462306a36Sopenharmony_ci	 * to be written into the hysteresis register. The datasheet defines
47562306a36Sopenharmony_ci	 * modes "A", "B1" and "B2" with fixed values to be use but vendor
47662306a36Sopenharmony_ci	 * code trees for actual devices are tweaking these values and refer to
47762306a36Sopenharmony_ci	 * modes named things like "B1.5". To be able to support any devices,
47862306a36Sopenharmony_ci	 * we allow passing an arbitrary hysteresis setting for "near" and
47962306a36Sopenharmony_ci	 * "far".
48062306a36Sopenharmony_ci	 */
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* Check the device tree for the IR LED hysteresis */
48362306a36Sopenharmony_ci	ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
48462306a36Sopenharmony_ci				      &val);
48562306a36Sopenharmony_ci	if (ret) {
48662306a36Sopenharmony_ci		dev_err(dev, "failed to obtain proximity far setting\n");
48762306a36Sopenharmony_ci		return ret;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci	dev_dbg(dev, "proximity far setting %02x\n", val);
49062306a36Sopenharmony_ci	gp2ap002->hys_far = val;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
49362306a36Sopenharmony_ci				      &val);
49462306a36Sopenharmony_ci	if (ret) {
49562306a36Sopenharmony_ci		dev_err(dev, "failed to obtain proximity close setting\n");
49662306a36Sopenharmony_ci		return ret;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	dev_dbg(dev, "proximity close setting %02x\n", val);
49962306a36Sopenharmony_ci	gp2ap002->hys_close = val;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* The GP2AP002A00F has a light sensor too */
50262306a36Sopenharmony_ci	if (!gp2ap002->is_gp2ap002s00f) {
50362306a36Sopenharmony_ci		gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
50462306a36Sopenharmony_ci		if (IS_ERR(gp2ap002->alsout)) {
50562306a36Sopenharmony_ci			ret = PTR_ERR(gp2ap002->alsout);
50662306a36Sopenharmony_ci			ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret;
50762306a36Sopenharmony_ci			return dev_err_probe(dev, ret, "failed to get ALSOUT ADC channel\n");
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci		ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
51062306a36Sopenharmony_ci		if (ret < 0)
51162306a36Sopenharmony_ci			return ret;
51262306a36Sopenharmony_ci		if (ch_type != IIO_CURRENT) {
51362306a36Sopenharmony_ci			dev_err(dev,
51462306a36Sopenharmony_ci				"wrong type of IIO channel specified for ALSOUT\n");
51562306a36Sopenharmony_ci			return -EINVAL;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	gp2ap002->vdd = devm_regulator_get(dev, "vdd");
52062306a36Sopenharmony_ci	if (IS_ERR(gp2ap002->vdd))
52162306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(gp2ap002->vdd),
52262306a36Sopenharmony_ci				     "failed to get VDD regulator\n");
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	gp2ap002->vio = devm_regulator_get(dev, "vio");
52562306a36Sopenharmony_ci	if (IS_ERR(gp2ap002->vio))
52662306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(gp2ap002->vio),
52762306a36Sopenharmony_ci				     "failed to get VIO regulator\n");
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* Operating voltage 2.4V .. 3.6V according to datasheet */
53062306a36Sopenharmony_ci	ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
53162306a36Sopenharmony_ci	if (ret) {
53262306a36Sopenharmony_ci		dev_err(dev, "failed to sett VDD voltage\n");
53362306a36Sopenharmony_ci		return ret;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* VIO should be between 1.65V and VDD */
53762306a36Sopenharmony_ci	ret = regulator_get_voltage(gp2ap002->vdd);
53862306a36Sopenharmony_ci	if (ret < 0) {
53962306a36Sopenharmony_ci		dev_err(dev, "failed to get VDD voltage\n");
54062306a36Sopenharmony_ci		return ret;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
54362306a36Sopenharmony_ci	if (ret) {
54462306a36Sopenharmony_ci		dev_err(dev, "failed to set VIO voltage\n");
54562306a36Sopenharmony_ci		return ret;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	ret = regulator_enable(gp2ap002->vdd);
54962306a36Sopenharmony_ci	if (ret) {
55062306a36Sopenharmony_ci		dev_err(dev, "failed to enable VDD regulator\n");
55162306a36Sopenharmony_ci		return ret;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	ret = regulator_enable(gp2ap002->vio);
55462306a36Sopenharmony_ci	if (ret) {
55562306a36Sopenharmony_ci		dev_err(dev, "failed to enable VIO regulator\n");
55662306a36Sopenharmony_ci		goto out_disable_vdd;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	msleep(20);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/*
56262306a36Sopenharmony_ci	 * Initialize the device and signal to runtime PM that now we are
56362306a36Sopenharmony_ci	 * definitely up and using power.
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci	ret = gp2ap002_init(gp2ap002);
56662306a36Sopenharmony_ci	if (ret) {
56762306a36Sopenharmony_ci		dev_err(dev, "initialization failed\n");
56862306a36Sopenharmony_ci		goto out_disable_vio;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci	pm_runtime_get_noresume(dev);
57162306a36Sopenharmony_ci	pm_runtime_set_active(dev);
57262306a36Sopenharmony_ci	pm_runtime_enable(dev);
57362306a36Sopenharmony_ci	gp2ap002->enabled = false;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, client->irq, NULL,
57662306a36Sopenharmony_ci					gp2ap002_prox_irq, IRQF_ONESHOT,
57762306a36Sopenharmony_ci					"gp2ap002", indio_dev);
57862306a36Sopenharmony_ci	if (ret) {
57962306a36Sopenharmony_ci		dev_err(dev, "unable to request IRQ\n");
58062306a36Sopenharmony_ci		goto out_put_pm;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	gp2ap002->irq = client->irq;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/*
58562306a36Sopenharmony_ci	 * As the device takes 20 ms + regulator delay to come up with a fresh
58662306a36Sopenharmony_ci	 * measurement after power-on, do not shut it down unnecessarily.
58762306a36Sopenharmony_ci	 * Set autosuspend to a one second.
58862306a36Sopenharmony_ci	 */
58962306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 1000);
59062306a36Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
59162306a36Sopenharmony_ci	pm_runtime_put(dev);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	indio_dev->info = &gp2ap002_info;
59462306a36Sopenharmony_ci	indio_dev->name = "gp2ap002";
59562306a36Sopenharmony_ci	indio_dev->channels = gp2ap002_channels;
59662306a36Sopenharmony_ci	/* Skip light channel for the proximity-only sensor */
59762306a36Sopenharmony_ci	num_chan = ARRAY_SIZE(gp2ap002_channels);
59862306a36Sopenharmony_ci	if (gp2ap002->is_gp2ap002s00f)
59962306a36Sopenharmony_ci		num_chan--;
60062306a36Sopenharmony_ci	indio_dev->num_channels = num_chan;
60162306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
60462306a36Sopenharmony_ci	if (ret)
60562306a36Sopenharmony_ci		goto out_disable_pm;
60662306a36Sopenharmony_ci	dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ciout_put_pm:
61162306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
61262306a36Sopenharmony_ciout_disable_pm:
61362306a36Sopenharmony_ci	pm_runtime_disable(dev);
61462306a36Sopenharmony_ciout_disable_vio:
61562306a36Sopenharmony_ci	regulator_disable(gp2ap002->vio);
61662306a36Sopenharmony_ciout_disable_vdd:
61762306a36Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
61862306a36Sopenharmony_ci	return ret;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic void gp2ap002_remove(struct i2c_client *client)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
62462306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
62562306a36Sopenharmony_ci	struct device *dev = &client->dev;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
62862306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
62962306a36Sopenharmony_ci	pm_runtime_disable(dev);
63062306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
63162306a36Sopenharmony_ci	regulator_disable(gp2ap002->vio);
63262306a36Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int gp2ap002_runtime_suspend(struct device *dev)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
63862306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
63962306a36Sopenharmony_ci	int ret;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* Deactivate the IRQ */
64262306a36Sopenharmony_ci	disable_irq(gp2ap002->irq);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/* Disable chip and IRQ, everything off */
64562306a36Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
64662306a36Sopenharmony_ci	if (ret) {
64762306a36Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up operation mode\n");
64862306a36Sopenharmony_ci		return ret;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	/*
65162306a36Sopenharmony_ci	 * As these regulators may be shared, at least we are now in
65262306a36Sopenharmony_ci	 * sleep even if the regulators aren't really turned off.
65362306a36Sopenharmony_ci	 */
65462306a36Sopenharmony_ci	regulator_disable(gp2ap002->vio);
65562306a36Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return 0;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int gp2ap002_runtime_resume(struct device *dev)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
66362306a36Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
66462306a36Sopenharmony_ci	int ret;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	ret = regulator_enable(gp2ap002->vdd);
66762306a36Sopenharmony_ci	if (ret) {
66862306a36Sopenharmony_ci		dev_err(dev, "failed to enable VDD regulator in resume path\n");
66962306a36Sopenharmony_ci		return ret;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	ret = regulator_enable(gp2ap002->vio);
67262306a36Sopenharmony_ci	if (ret) {
67362306a36Sopenharmony_ci		dev_err(dev, "failed to enable VIO regulator in resume path\n");
67462306a36Sopenharmony_ci		return ret;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	msleep(20);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	ret = gp2ap002_init(gp2ap002);
68062306a36Sopenharmony_ci	if (ret) {
68162306a36Sopenharmony_ci		dev_err(dev, "re-initialization failed\n");
68262306a36Sopenharmony_ci		return ret;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* Re-activate the IRQ */
68662306a36Sopenharmony_ci	enable_irq(gp2ap002->irq);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend,
69262306a36Sopenharmony_ci				 gp2ap002_runtime_resume, NULL);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic const struct i2c_device_id gp2ap002_id_table[] = {
69562306a36Sopenharmony_ci	{ "gp2ap002", 0 },
69662306a36Sopenharmony_ci	{ },
69762306a36Sopenharmony_ci};
69862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, gp2ap002_id_table);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic const struct of_device_id gp2ap002_of_match[] = {
70162306a36Sopenharmony_ci	{ .compatible = "sharp,gp2ap002a00f" },
70262306a36Sopenharmony_ci	{ .compatible = "sharp,gp2ap002s00f" },
70362306a36Sopenharmony_ci	{ },
70462306a36Sopenharmony_ci};
70562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gp2ap002_of_match);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic struct i2c_driver gp2ap002_driver = {
70862306a36Sopenharmony_ci	.driver = {
70962306a36Sopenharmony_ci		.name = "gp2ap002",
71062306a36Sopenharmony_ci		.of_match_table = gp2ap002_of_match,
71162306a36Sopenharmony_ci		.pm = pm_ptr(&gp2ap002_dev_pm_ops),
71262306a36Sopenharmony_ci	},
71362306a36Sopenharmony_ci	.probe = gp2ap002_probe,
71462306a36Sopenharmony_ci	.remove = gp2ap002_remove,
71562306a36Sopenharmony_ci	.id_table = gp2ap002_id_table,
71662306a36Sopenharmony_ci};
71762306a36Sopenharmony_cimodule_i2c_driver(gp2ap002_driver);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
72062306a36Sopenharmony_ciMODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
72162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
722