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