18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * These are the two Sharp GP2AP002 variants supported by this driver:
48c2ecf20Sopenharmony_ci * GP2AP002A00F Ambient Light and Proximity Sensor
58c2ecf20Sopenharmony_ci * GP2AP002S00F Proximity Sensor
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2020 Linaro Ltd.
88c2ecf20Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Based partly on the code in Sony Ericssons GP2AP00200F driver by
118c2ecf20Sopenharmony_ci * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c
128c2ecf20Sopenharmony_ci * Based partly on a Samsung misc driver submitted by
138c2ecf20Sopenharmony_ci * Donggeun Kim & Minkyu Kang in 2011:
148c2ecf20Sopenharmony_ci * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/
158c2ecf20Sopenharmony_ci * Based partly on a submission by
168c2ecf20Sopenharmony_ci * Jonathan Bakker and Paweł Chmiel in january 2019:
178c2ecf20Sopenharmony_ci * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/
188c2ecf20Sopenharmony_ci * Based partly on code from the Samsung GT-S7710 by <mjchen@sta.samsung.com>
198c2ecf20Sopenharmony_ci * Based partly on the code in LG Electronics GP2AP00200F driver by
208c2ecf20Sopenharmony_ci * Kenobi Lee <sungyoung.lee@lge.com> and EunYoung Cho <ey.cho@lge.com>
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/i2c.h>
248c2ecf20Sopenharmony_ci#include <linux/regmap.h>
258c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
268c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
278c2ecf20Sopenharmony_ci#include <linux/iio/events.h>
288c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> /* To get our ADC channel */
298c2ecf20Sopenharmony_ci#include <linux/iio/types.h> /* To deal with our ADC channel */
308c2ecf20Sopenharmony_ci#include <linux/init.h>
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
338c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
348c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
358c2ecf20Sopenharmony_ci#include <linux/bits.h>
368c2ecf20Sopenharmony_ci#include <linux/math64.h>
378c2ecf20Sopenharmony_ci#include <linux/pm.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define GP2AP002_PROX_CHANNEL 0
408c2ecf20Sopenharmony_ci#define GP2AP002_ALS_CHANNEL 1
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
438c2ecf20Sopenharmony_ci/* ADDRESS SYMBOL             DATA                                 Init R/W */
448c2ecf20Sopenharmony_ci/*                   D7    D6    D5    D4    D3    D2    D1    D0           */
458c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
468c2ecf20Sopenharmony_ci/*    0      PROX     X     X     X     X     X     X     X    VO  H'00   R */
478c2ecf20Sopenharmony_ci/*    1      GAIN     X     X     X     X  LED0     X     X     X  H'00   W */
488c2ecf20Sopenharmony_ci/*    2       HYS  HYSD HYSC1 HYSC0     X HYSF3 HYSF2 HYSF1 HYSF0  H'00   W */
498c2ecf20Sopenharmony_ci/*    3     CYCLE     X     X CYCL2 CYCL1 CYCL0  OSC2     X     X  H'00   W */
508c2ecf20Sopenharmony_ci/*    4     OPMOD     X     X     X   ASD     X     X  VCON   SSD  H'00   W */
518c2ecf20Sopenharmony_ci/*    6       CON     X     X     X OCON1 OCON0     X     X     X  H'00   W */
528c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
538c2ecf20Sopenharmony_ci/* VO   :Proximity sensing result(0: no detection, 1: detection)            */
548c2ecf20Sopenharmony_ci/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/
558c2ecf20Sopenharmony_ci/* HYSD/HYSF :Adjusts the receiver sensitivity                              */
568c2ecf20Sopenharmony_ci/* OSC  :Select switch internal clocl frequency hoppling(0:effective)       */
578c2ecf20Sopenharmony_ci/* CYCL :Determine the detection cycle(typically 8ms, up to 128x)           */
588c2ecf20Sopenharmony_ci/* SSD  :Software Shutdown function(0:shutdown, 1:operating)                */
598c2ecf20Sopenharmony_ci/* VCON :VOUT output method control(0:normal, 1:interrupt)                  */
608c2ecf20Sopenharmony_ci/* ASD  :Select switch for analog sleep function(0:ineffective, 1:effective)*/
618c2ecf20Sopenharmony_ci/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable)  */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define GP2AP002_PROX				0x00
648c2ecf20Sopenharmony_ci#define GP2AP002_GAIN				0x01
658c2ecf20Sopenharmony_ci#define GP2AP002_HYS				0x02
668c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE				0x03
678c2ecf20Sopenharmony_ci#define GP2AP002_OPMOD				0x04
688c2ecf20Sopenharmony_ci#define GP2AP002_CON				0x06
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define GP2AP002_PROX_VO_DETECT			BIT(0)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Setting this bit to 0 means 2x higher LED resistance */
738c2ecf20Sopenharmony_ci#define GP2AP002_GAIN_LED_NORMAL		BIT(3)
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * These bits adjusts the proximity sensitivity, determining characteristics
778c2ecf20Sopenharmony_ci * of the detection distance and its hysteresis.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSD_SHIFT		7
808c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSD_MASK		BIT(7)
818c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSC_SHIFT		5
828c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSC_MASK		GENMASK(6, 5)
838c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSF_SHIFT		0
848c2ecf20Sopenharmony_ci#define GP2AP002_HYS_HYSF_MASK		GENMASK(3, 0)
858c2ecf20Sopenharmony_ci#define GP2AP002_HYS_MASK		(GP2AP002_HYS_HYSD_MASK | \
868c2ecf20Sopenharmony_ci					 GP2AP002_HYS_HYSC_MASK | \
878c2ecf20Sopenharmony_ci					 GP2AP002_HYS_HYSF_MASK)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/*
908c2ecf20Sopenharmony_ci * These values determine the detection cycle response time
918c2ecf20Sopenharmony_ci * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms,
928c2ecf20Sopenharmony_ci * 5: 256ms, 6: 512ms, 7: 1024ms
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE_CYCL_SHIFT	3
958c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE_CYCL_MASK	GENMASK(5, 3)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * Select switch for internal clock frequency hopping
998c2ecf20Sopenharmony_ci *	0: effective,
1008c2ecf20Sopenharmony_ci *	1: ineffective
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE_OSC_EFFECTIVE	0
1038c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE_OSC_INEFFECTIVE	BIT(2)
1048c2ecf20Sopenharmony_ci#define GP2AP002_CYCLE_OSC_MASK		BIT(2)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Analog sleep effective */
1078c2ecf20Sopenharmony_ci#define GP2AP002_OPMOD_ASD		BIT(4)
1088c2ecf20Sopenharmony_ci/* Enable chip */
1098c2ecf20Sopenharmony_ci#define GP2AP002_OPMOD_SSD_OPERATING	BIT(0)
1108c2ecf20Sopenharmony_ci/* IRQ mode */
1118c2ecf20Sopenharmony_ci#define GP2AP002_OPMOD_VCON_IRQ		BIT(1)
1128c2ecf20Sopenharmony_ci#define GP2AP002_OPMOD_MASK		(BIT(0) | BIT(1) | BIT(4))
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/*
1158c2ecf20Sopenharmony_ci * Select switch for enabling/disabling Vout pin
1168c2ecf20Sopenharmony_ci * 0: enable
1178c2ecf20Sopenharmony_ci * 2: force to go Low
1188c2ecf20Sopenharmony_ci * 3: force to go High
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ci#define GP2AP002_CON_OCON_SHIFT		3
1218c2ecf20Sopenharmony_ci#define GP2AP002_CON_OCON_ENABLE	(0x0 << GP2AP002_CON_OCON_SHIFT)
1228c2ecf20Sopenharmony_ci#define GP2AP002_CON_OCON_LOW		(0x2 << GP2AP002_CON_OCON_SHIFT)
1238c2ecf20Sopenharmony_ci#define GP2AP002_CON_OCON_HIGH		(0x3 << GP2AP002_CON_OCON_SHIFT)
1248c2ecf20Sopenharmony_ci#define GP2AP002_CON_OCON_MASK		(0x3 << GP2AP002_CON_OCON_SHIFT)
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * struct gp2ap002 - GP2AP002 state
1288c2ecf20Sopenharmony_ci * @map: regmap pointer for the i2c regmap
1298c2ecf20Sopenharmony_ci * @dev: pointer to parent device
1308c2ecf20Sopenharmony_ci * @vdd: regulator controlling VDD
1318c2ecf20Sopenharmony_ci * @vio: regulator controlling VIO
1328c2ecf20Sopenharmony_ci * @alsout: IIO ADC channel to convert the ALSOUT signal
1338c2ecf20Sopenharmony_ci * @hys_far: hysteresis control from device tree
1348c2ecf20Sopenharmony_ci * @hys_close: hysteresis control from device tree
1358c2ecf20Sopenharmony_ci * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip
1368c2ecf20Sopenharmony_ci * @irq: the IRQ line used by this device
1378c2ecf20Sopenharmony_ci * @enabled: we cannot read the status of the hardware so we need to
1388c2ecf20Sopenharmony_ci * keep track of whether the event is enabled using this state variable
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistruct gp2ap002 {
1418c2ecf20Sopenharmony_ci	struct regmap *map;
1428c2ecf20Sopenharmony_ci	struct device *dev;
1438c2ecf20Sopenharmony_ci	struct regulator *vdd;
1448c2ecf20Sopenharmony_ci	struct regulator *vio;
1458c2ecf20Sopenharmony_ci	struct iio_channel *alsout;
1468c2ecf20Sopenharmony_ci	u8 hys_far;
1478c2ecf20Sopenharmony_ci	u8 hys_close;
1488c2ecf20Sopenharmony_ci	bool is_gp2ap002s00f;
1498c2ecf20Sopenharmony_ci	int irq;
1508c2ecf20Sopenharmony_ci	bool enabled;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic irqreturn_t gp2ap002_prox_irq(int irq, void *d)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = d;
1568c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
1578c2ecf20Sopenharmony_ci	u64 ev;
1588c2ecf20Sopenharmony_ci	int val;
1598c2ecf20Sopenharmony_ci	int ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!gp2ap002->enabled)
1628c2ecf20Sopenharmony_ci		goto err_retrig;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val);
1658c2ecf20Sopenharmony_ci	if (ret) {
1668c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error reading proximity\n");
1678c2ecf20Sopenharmony_ci		goto err_retrig;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (val & GP2AP002_PROX_VO_DETECT) {
1718c2ecf20Sopenharmony_ci		/* Close */
1728c2ecf20Sopenharmony_ci		dev_dbg(gp2ap002->dev, "close\n");
1738c2ecf20Sopenharmony_ci		ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
1748c2ecf20Sopenharmony_ci				   gp2ap002->hys_far);
1758c2ecf20Sopenharmony_ci		if (ret)
1768c2ecf20Sopenharmony_ci			dev_err(gp2ap002->dev,
1778c2ecf20Sopenharmony_ci				"error setting up proximity hysteresis\n");
1788c2ecf20Sopenharmony_ci		ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
1798c2ecf20Sopenharmony_ci					IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
1808c2ecf20Sopenharmony_ci	} else {
1818c2ecf20Sopenharmony_ci		/* Far */
1828c2ecf20Sopenharmony_ci		dev_dbg(gp2ap002->dev, "far\n");
1838c2ecf20Sopenharmony_ci		ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
1848c2ecf20Sopenharmony_ci				   gp2ap002->hys_close);
1858c2ecf20Sopenharmony_ci		if (ret)
1868c2ecf20Sopenharmony_ci			dev_err(gp2ap002->dev,
1878c2ecf20Sopenharmony_ci				"error setting up proximity hysteresis\n");
1888c2ecf20Sopenharmony_ci		ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
1898c2ecf20Sopenharmony_ci					IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/*
1948c2ecf20Sopenharmony_ci	 * After changing hysteresis, we need to wait for one detection
1958c2ecf20Sopenharmony_ci	 * cycle to see if anything changed, or we will just trigger the
1968c2ecf20Sopenharmony_ci	 * previous interrupt again. A detection cycle depends on the CYCLE
1978c2ecf20Sopenharmony_ci	 * register, we are hard-coding ~8 ms in probe() so wait some more
1988c2ecf20Sopenharmony_ci	 * than this, 20-30 ms.
1998c2ecf20Sopenharmony_ci	 */
2008c2ecf20Sopenharmony_ci	usleep_range(20000, 30000);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cierr_retrig:
2038c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CON,
2048c2ecf20Sopenharmony_ci			   GP2AP002_CON_OCON_ENABLE);
2058c2ecf20Sopenharmony_ci	if (ret)
2068c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up VOUT control\n");
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/*
2128c2ecf20Sopenharmony_ci * This array maps current and lux.
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * Ambient light sensing range is 3 to 55000 lux.
2158c2ecf20Sopenharmony_ci *
2168c2ecf20Sopenharmony_ci * This mapping is based on the following formula.
2178c2ecf20Sopenharmony_ci * illuminance = 10 ^ (current[mA] / 10)
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * When the ADC measures 0, return 0 lux.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic const u16 gp2ap002_illuminance_table[] = {
2228c2ecf20Sopenharmony_ci	0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79,
2238c2ecf20Sopenharmony_ci	100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585,
2248c2ecf20Sopenharmony_ci	1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953,
2258c2ecf20Sopenharmony_ci	25119, 31623, 39811, 50119,
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	int ret, res;
2318c2ecf20Sopenharmony_ci	u16 lux;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ret = iio_read_channel_processed(gp2ap002->alsout, &res);
2348c2ecf20Sopenharmony_ci	if (ret < 0)
2358c2ecf20Sopenharmony_ci		return ret;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* ensure we don't under/overflow */
2408c2ecf20Sopenharmony_ci	res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1);
2418c2ecf20Sopenharmony_ci	lux = gp2ap002_illuminance_table[res];
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return (int)lux;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int gp2ap002_read_raw(struct iio_dev *indio_dev,
2478c2ecf20Sopenharmony_ci			   struct iio_chan_spec const *chan,
2488c2ecf20Sopenharmony_ci			   int *val, int *val2, long mask)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
2518c2ecf20Sopenharmony_ci	int ret;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	pm_runtime_get_sync(gp2ap002->dev);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	switch (mask) {
2568c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
2578c2ecf20Sopenharmony_ci		switch (chan->type) {
2588c2ecf20Sopenharmony_ci		case IIO_LIGHT:
2598c2ecf20Sopenharmony_ci			ret = gp2ap002_get_lux(gp2ap002);
2608c2ecf20Sopenharmony_ci			if (ret < 0)
2618c2ecf20Sopenharmony_ci				return ret;
2628c2ecf20Sopenharmony_ci			*val = ret;
2638c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT;
2648c2ecf20Sopenharmony_ci			goto out;
2658c2ecf20Sopenharmony_ci		default:
2668c2ecf20Sopenharmony_ci			ret = -EINVAL;
2678c2ecf20Sopenharmony_ci			goto out;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci	default:
2708c2ecf20Sopenharmony_ci		ret = -EINVAL;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciout:
2748c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(gp2ap002->dev);
2758c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(gp2ap002->dev);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return ret;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int gp2ap002_init(struct gp2ap002 *gp2ap002)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int ret;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* Set up the IR LED resistance */
2858c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_GAIN,
2868c2ecf20Sopenharmony_ci			   GP2AP002_GAIN_LED_NORMAL);
2878c2ecf20Sopenharmony_ci	if (ret) {
2888c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up LED gain\n");
2898c2ecf20Sopenharmony_ci		return ret;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far);
2928c2ecf20Sopenharmony_ci	if (ret) {
2938c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev,
2948c2ecf20Sopenharmony_ci			"error setting up proximity hysteresis\n");
2958c2ecf20Sopenharmony_ci		return ret;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Disable internal frequency hopping */
2998c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE,
3008c2ecf20Sopenharmony_ci			   GP2AP002_CYCLE_OSC_INEFFECTIVE);
3018c2ecf20Sopenharmony_ci	if (ret) {
3028c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev,
3038c2ecf20Sopenharmony_ci			"error setting up internal frequency hopping\n");
3048c2ecf20Sopenharmony_ci		return ret;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Enable chip and IRQ, disable analog sleep */
3088c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD,
3098c2ecf20Sopenharmony_ci			   GP2AP002_OPMOD_SSD_OPERATING |
3108c2ecf20Sopenharmony_ci			   GP2AP002_OPMOD_VCON_IRQ);
3118c2ecf20Sopenharmony_ci	if (ret) {
3128c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up operation mode\n");
3138c2ecf20Sopenharmony_ci		return ret;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Interrupt on VOUT enabled */
3178c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_CON,
3188c2ecf20Sopenharmony_ci			   GP2AP002_CON_OCON_ENABLE);
3198c2ecf20Sopenharmony_ci	if (ret)
3208c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up VOUT control\n");
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return ret;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int gp2ap002_read_event_config(struct iio_dev *indio_dev,
3268c2ecf20Sopenharmony_ci				      const struct iio_chan_spec *chan,
3278c2ecf20Sopenharmony_ci				      enum iio_event_type type,
3288c2ecf20Sopenharmony_ci				      enum iio_event_direction dir)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/*
3338c2ecf20Sopenharmony_ci	 * We just keep track of this internally, as it is not possible to
3348c2ecf20Sopenharmony_ci	 * query the hardware.
3358c2ecf20Sopenharmony_ci	 */
3368c2ecf20Sopenharmony_ci	return gp2ap002->enabled;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int gp2ap002_write_event_config(struct iio_dev *indio_dev,
3408c2ecf20Sopenharmony_ci				       const struct iio_chan_spec *chan,
3418c2ecf20Sopenharmony_ci				       enum iio_event_type type,
3428c2ecf20Sopenharmony_ci				       enum iio_event_direction dir,
3438c2ecf20Sopenharmony_ci				       int state)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (state) {
3488c2ecf20Sopenharmony_ci		/*
3498c2ecf20Sopenharmony_ci		 * This will bring the regulators up (unless they are on
3508c2ecf20Sopenharmony_ci		 * already) and reintialize the sensor by using runtime_pm
3518c2ecf20Sopenharmony_ci		 * callbacks.
3528c2ecf20Sopenharmony_ci		 */
3538c2ecf20Sopenharmony_ci		pm_runtime_get_sync(gp2ap002->dev);
3548c2ecf20Sopenharmony_ci		gp2ap002->enabled = true;
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(gp2ap002->dev);
3578c2ecf20Sopenharmony_ci		pm_runtime_put_autosuspend(gp2ap002->dev);
3588c2ecf20Sopenharmony_ci		gp2ap002->enabled = false;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return 0;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic const struct iio_info gp2ap002_info = {
3658c2ecf20Sopenharmony_ci	.read_raw = gp2ap002_read_raw,
3668c2ecf20Sopenharmony_ci	.read_event_config = gp2ap002_read_event_config,
3678c2ecf20Sopenharmony_ci	.write_event_config = gp2ap002_write_event_config,
3688c2ecf20Sopenharmony_ci};
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const struct iio_event_spec gp2ap002_events[] = {
3718c2ecf20Sopenharmony_ci	{
3728c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
3738c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_EITHER,
3748c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
3758c2ecf20Sopenharmony_ci	},
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic const struct iio_chan_spec gp2ap002_channels[] = {
3798c2ecf20Sopenharmony_ci	{
3808c2ecf20Sopenharmony_ci		.type = IIO_PROXIMITY,
3818c2ecf20Sopenharmony_ci		.event_spec = gp2ap002_events,
3828c2ecf20Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(gp2ap002_events),
3838c2ecf20Sopenharmony_ci	},
3848c2ecf20Sopenharmony_ci	{
3858c2ecf20Sopenharmony_ci		.type = IIO_LIGHT,
3868c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
3878c2ecf20Sopenharmony_ci		.channel = GP2AP002_ALS_CHANNEL,
3888c2ecf20Sopenharmony_ci	},
3898c2ecf20Sopenharmony_ci};
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/*
3928c2ecf20Sopenharmony_ci * We need a special regmap because this hardware expects to
3938c2ecf20Sopenharmony_ci * write single bytes to registers but read a 16bit word on some
3948c2ecf20Sopenharmony_ci * variants and discard the lower 8 bits so combine
3958c2ecf20Sopenharmony_ci * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data()
3968c2ecf20Sopenharmony_ci * selectively like this.
3978c2ecf20Sopenharmony_ci */
3988c2ecf20Sopenharmony_cistatic int gp2ap002_regmap_i2c_read(void *context, unsigned int reg,
3998c2ecf20Sopenharmony_ci				    unsigned int *val)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct device *dev = context;
4028c2ecf20Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
4038c2ecf20Sopenharmony_ci	int ret;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(i2c, reg);
4068c2ecf20Sopenharmony_ci	if (ret < 0)
4078c2ecf20Sopenharmony_ci		return ret;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	*val = (ret >> 8) & 0xFF;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
4158c2ecf20Sopenharmony_ci				     unsigned int val)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct device *dev = context;
4188c2ecf20Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(i2c, reg, val);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic struct regmap_bus gp2ap002_regmap_bus = {
4248c2ecf20Sopenharmony_ci	.reg_read = gp2ap002_regmap_i2c_read,
4258c2ecf20Sopenharmony_ci	.reg_write = gp2ap002_regmap_i2c_write,
4268c2ecf20Sopenharmony_ci};
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int gp2ap002_probe(struct i2c_client *client,
4298c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002;
4328c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
4338c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
4348c2ecf20Sopenharmony_ci	enum iio_chan_type ch_type;
4358c2ecf20Sopenharmony_ci	static const struct regmap_config config = {
4368c2ecf20Sopenharmony_ci		.reg_bits = 8,
4378c2ecf20Sopenharmony_ci		.val_bits = 8,
4388c2ecf20Sopenharmony_ci		.max_register = GP2AP002_CON,
4398c2ecf20Sopenharmony_ci	};
4408c2ecf20Sopenharmony_ci	struct regmap *regmap;
4418c2ecf20Sopenharmony_ci	int num_chan;
4428c2ecf20Sopenharmony_ci	const char *compat;
4438c2ecf20Sopenharmony_ci	u8 val;
4448c2ecf20Sopenharmony_ci	int ret;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
4478c2ecf20Sopenharmony_ci	if (!indio_dev)
4488c2ecf20Sopenharmony_ci		return -ENOMEM;
4498c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	gp2ap002 = iio_priv(indio_dev);
4528c2ecf20Sopenharmony_ci	gp2ap002->dev = dev;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/*
4558c2ecf20Sopenharmony_ci	 * Check the device compatible like this makes it possible to use
4568c2ecf20Sopenharmony_ci	 * ACPI PRP0001 for registering the sensor using device tree
4578c2ecf20Sopenharmony_ci	 * properties.
4588c2ecf20Sopenharmony_ci	 */
4598c2ecf20Sopenharmony_ci	ret = device_property_read_string(dev, "compatible", &compat);
4608c2ecf20Sopenharmony_ci	if (ret) {
4618c2ecf20Sopenharmony_ci		dev_err(dev, "cannot check compatible\n");
4628c2ecf20Sopenharmony_ci		return ret;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
4678c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
4688c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register i2c regmap %d\n",
4698c2ecf20Sopenharmony_ci			(int)PTR_ERR(regmap));
4708c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci	gp2ap002->map = regmap;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/*
4758c2ecf20Sopenharmony_ci	 * The hysteresis settings are coded into the device tree as values
4768c2ecf20Sopenharmony_ci	 * to be written into the hysteresis register. The datasheet defines
4778c2ecf20Sopenharmony_ci	 * modes "A", "B1" and "B2" with fixed values to be use but vendor
4788c2ecf20Sopenharmony_ci	 * code trees for actual devices are tweaking these values and refer to
4798c2ecf20Sopenharmony_ci	 * modes named things like "B1.5". To be able to support any devices,
4808c2ecf20Sopenharmony_ci	 * we allow passing an arbitrary hysteresis setting for "near" and
4818c2ecf20Sopenharmony_ci	 * "far".
4828c2ecf20Sopenharmony_ci	 */
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* Check the device tree for the IR LED hysteresis */
4858c2ecf20Sopenharmony_ci	ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
4868c2ecf20Sopenharmony_ci				      &val);
4878c2ecf20Sopenharmony_ci	if (ret) {
4888c2ecf20Sopenharmony_ci		dev_err(dev, "failed to obtain proximity far setting\n");
4898c2ecf20Sopenharmony_ci		return ret;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci	dev_dbg(dev, "proximity far setting %02x\n", val);
4928c2ecf20Sopenharmony_ci	gp2ap002->hys_far = val;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
4958c2ecf20Sopenharmony_ci				      &val);
4968c2ecf20Sopenharmony_ci	if (ret) {
4978c2ecf20Sopenharmony_ci		dev_err(dev, "failed to obtain proximity close setting\n");
4988c2ecf20Sopenharmony_ci		return ret;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci	dev_dbg(dev, "proximity close setting %02x\n", val);
5018c2ecf20Sopenharmony_ci	gp2ap002->hys_close = val;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* The GP2AP002A00F has a light sensor too */
5048c2ecf20Sopenharmony_ci	if (!gp2ap002->is_gp2ap002s00f) {
5058c2ecf20Sopenharmony_ci		gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
5068c2ecf20Sopenharmony_ci		if (IS_ERR(gp2ap002->alsout)) {
5078c2ecf20Sopenharmony_ci			if (PTR_ERR(gp2ap002->alsout) == -ENODEV) {
5088c2ecf20Sopenharmony_ci				dev_err(dev, "no ADC, deferring...\n");
5098c2ecf20Sopenharmony_ci				return -EPROBE_DEFER;
5108c2ecf20Sopenharmony_ci			}
5118c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get ALSOUT ADC channel\n");
5128c2ecf20Sopenharmony_ci			return PTR_ERR(gp2ap002->alsout);
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci		ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
5158c2ecf20Sopenharmony_ci		if (ret < 0)
5168c2ecf20Sopenharmony_ci			return ret;
5178c2ecf20Sopenharmony_ci		if (ch_type != IIO_CURRENT) {
5188c2ecf20Sopenharmony_ci			dev_err(dev,
5198c2ecf20Sopenharmony_ci				"wrong type of IIO channel specified for ALSOUT\n");
5208c2ecf20Sopenharmony_ci			return -EINVAL;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	gp2ap002->vdd = devm_regulator_get(dev, "vdd");
5258c2ecf20Sopenharmony_ci	if (IS_ERR(gp2ap002->vdd)) {
5268c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get VDD regulator\n");
5278c2ecf20Sopenharmony_ci		return PTR_ERR(gp2ap002->vdd);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	gp2ap002->vio = devm_regulator_get(dev, "vio");
5308c2ecf20Sopenharmony_ci	if (IS_ERR(gp2ap002->vio)) {
5318c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get VIO regulator\n");
5328c2ecf20Sopenharmony_ci		return PTR_ERR(gp2ap002->vio);
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* Operating voltage 2.4V .. 3.6V according to datasheet */
5368c2ecf20Sopenharmony_ci	ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
5378c2ecf20Sopenharmony_ci	if (ret) {
5388c2ecf20Sopenharmony_ci		dev_err(dev, "failed to sett VDD voltage\n");
5398c2ecf20Sopenharmony_ci		return ret;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	/* VIO should be between 1.65V and VDD */
5438c2ecf20Sopenharmony_ci	ret = regulator_get_voltage(gp2ap002->vdd);
5448c2ecf20Sopenharmony_ci	if (ret < 0) {
5458c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get VDD voltage\n");
5468c2ecf20Sopenharmony_ci		return ret;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci	ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
5498c2ecf20Sopenharmony_ci	if (ret) {
5508c2ecf20Sopenharmony_ci		dev_err(dev, "failed to set VIO voltage\n");
5518c2ecf20Sopenharmony_ci		return ret;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	ret = regulator_enable(gp2ap002->vdd);
5558c2ecf20Sopenharmony_ci	if (ret) {
5568c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable VDD regulator\n");
5578c2ecf20Sopenharmony_ci		return ret;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	ret = regulator_enable(gp2ap002->vio);
5608c2ecf20Sopenharmony_ci	if (ret) {
5618c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable VIO regulator\n");
5628c2ecf20Sopenharmony_ci		goto out_disable_vdd;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	msleep(20);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/*
5688c2ecf20Sopenharmony_ci	 * Initialize the device and signal to runtime PM that now we are
5698c2ecf20Sopenharmony_ci	 * definitely up and using power.
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	ret = gp2ap002_init(gp2ap002);
5728c2ecf20Sopenharmony_ci	if (ret) {
5738c2ecf20Sopenharmony_ci		dev_err(dev, "initialization failed\n");
5748c2ecf20Sopenharmony_ci		goto out_disable_vio;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(dev);
5778c2ecf20Sopenharmony_ci	pm_runtime_set_active(dev);
5788c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
5798c2ecf20Sopenharmony_ci	gp2ap002->enabled = false;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(dev, client->irq, NULL,
5828c2ecf20Sopenharmony_ci					gp2ap002_prox_irq, IRQF_ONESHOT,
5838c2ecf20Sopenharmony_ci					"gp2ap002", indio_dev);
5848c2ecf20Sopenharmony_ci	if (ret) {
5858c2ecf20Sopenharmony_ci		dev_err(dev, "unable to request IRQ\n");
5868c2ecf20Sopenharmony_ci		goto out_put_pm;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	gp2ap002->irq = client->irq;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/*
5918c2ecf20Sopenharmony_ci	 * As the device takes 20 ms + regulator delay to come up with a fresh
5928c2ecf20Sopenharmony_ci	 * measurement after power-on, do not shut it down unnecessarily.
5938c2ecf20Sopenharmony_ci	 * Set autosuspend to a one second.
5948c2ecf20Sopenharmony_ci	 */
5958c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 1000);
5968c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
5978c2ecf20Sopenharmony_ci	pm_runtime_put(dev);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	indio_dev->info = &gp2ap002_info;
6008c2ecf20Sopenharmony_ci	indio_dev->name = "gp2ap002";
6018c2ecf20Sopenharmony_ci	indio_dev->channels = gp2ap002_channels;
6028c2ecf20Sopenharmony_ci	/* Skip light channel for the proximity-only sensor */
6038c2ecf20Sopenharmony_ci	num_chan = ARRAY_SIZE(gp2ap002_channels);
6048c2ecf20Sopenharmony_ci	if (gp2ap002->is_gp2ap002s00f)
6058c2ecf20Sopenharmony_ci		num_chan--;
6068c2ecf20Sopenharmony_ci	indio_dev->num_channels = num_chan;
6078c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	ret = iio_device_register(indio_dev);
6108c2ecf20Sopenharmony_ci	if (ret)
6118c2ecf20Sopenharmony_ci		goto out_disable_pm;
6128c2ecf20Sopenharmony_ci	dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return 0;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ciout_put_pm:
6178c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(dev);
6188c2ecf20Sopenharmony_ciout_disable_pm:
6198c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
6208c2ecf20Sopenharmony_ciout_disable_vio:
6218c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vio);
6228c2ecf20Sopenharmony_ciout_disable_vdd:
6238c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
6248c2ecf20Sopenharmony_ci	return ret;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic int gp2ap002_remove(struct i2c_client *client)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
6308c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
6318c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dev);
6348c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(dev);
6358c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
6368c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
6378c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vio);
6388c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return 0;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int __maybe_unused gp2ap002_runtime_suspend(struct device *dev)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
6468c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
6478c2ecf20Sopenharmony_ci	int ret;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* Deactivate the IRQ */
6508c2ecf20Sopenharmony_ci	disable_irq(gp2ap002->irq);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	/* Disable chip and IRQ, everything off */
6538c2ecf20Sopenharmony_ci	ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
6548c2ecf20Sopenharmony_ci	if (ret) {
6558c2ecf20Sopenharmony_ci		dev_err(gp2ap002->dev, "error setting up operation mode\n");
6568c2ecf20Sopenharmony_ci		return ret;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci	/*
6598c2ecf20Sopenharmony_ci	 * As these regulators may be shared, at least we are now in
6608c2ecf20Sopenharmony_ci	 * sleep even if the regulators aren't really turned off.
6618c2ecf20Sopenharmony_ci	 */
6628c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vio);
6638c2ecf20Sopenharmony_ci	regulator_disable(gp2ap002->vdd);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return 0;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int __maybe_unused gp2ap002_runtime_resume(struct device *dev)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
6718c2ecf20Sopenharmony_ci	struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
6728c2ecf20Sopenharmony_ci	int ret;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	ret = regulator_enable(gp2ap002->vdd);
6758c2ecf20Sopenharmony_ci	if (ret) {
6768c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable VDD regulator in resume path\n");
6778c2ecf20Sopenharmony_ci		return ret;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	ret = regulator_enable(gp2ap002->vio);
6808c2ecf20Sopenharmony_ci	if (ret) {
6818c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable VIO regulator in resume path\n");
6828c2ecf20Sopenharmony_ci		return ret;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	msleep(20);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	ret = gp2ap002_init(gp2ap002);
6888c2ecf20Sopenharmony_ci	if (ret) {
6898c2ecf20Sopenharmony_ci		dev_err(dev, "re-initialization failed\n");
6908c2ecf20Sopenharmony_ci		return ret;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* Re-activate the IRQ */
6948c2ecf20Sopenharmony_ci	enable_irq(gp2ap002->irq);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return 0;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic const struct dev_pm_ops gp2ap002_dev_pm_ops = {
7008c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
7018c2ecf20Sopenharmony_ci				pm_runtime_force_resume)
7028c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(gp2ap002_runtime_suspend,
7038c2ecf20Sopenharmony_ci			   gp2ap002_runtime_resume, NULL)
7048c2ecf20Sopenharmony_ci};
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic const struct i2c_device_id gp2ap002_id_table[] = {
7078c2ecf20Sopenharmony_ci	{ "gp2ap002", 0 },
7088c2ecf20Sopenharmony_ci	{ },
7098c2ecf20Sopenharmony_ci};
7108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, gp2ap002_id_table);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic const struct of_device_id gp2ap002_of_match[] = {
7138c2ecf20Sopenharmony_ci	{ .compatible = "sharp,gp2ap002a00f" },
7148c2ecf20Sopenharmony_ci	{ .compatible = "sharp,gp2ap002s00f" },
7158c2ecf20Sopenharmony_ci	{ },
7168c2ecf20Sopenharmony_ci};
7178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gp2ap002_of_match);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic struct i2c_driver gp2ap002_driver = {
7208c2ecf20Sopenharmony_ci	.driver = {
7218c2ecf20Sopenharmony_ci		.name = "gp2ap002",
7228c2ecf20Sopenharmony_ci		.of_match_table = gp2ap002_of_match,
7238c2ecf20Sopenharmony_ci		.pm = &gp2ap002_dev_pm_ops,
7248c2ecf20Sopenharmony_ci	},
7258c2ecf20Sopenharmony_ci	.probe = gp2ap002_probe,
7268c2ecf20Sopenharmony_ci	.remove = gp2ap002_remove,
7278c2ecf20Sopenharmony_ci	.id_table = gp2ap002_id_table,
7288c2ecf20Sopenharmony_ci};
7298c2ecf20Sopenharmony_cimodule_i2c_driver(gp2ap002_driver);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
7328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
7338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
734