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