1*3422b4bcSDuje Mihanović // SPDX-License-Identifier: GPL-2.0-only 2*3422b4bcSDuje Mihanović /* 3*3422b4bcSDuje Mihanović * Copyright 2025, Duje Mihanović <duje@dujemihanovic.xyz> 4*3422b4bcSDuje Mihanović */ 5*3422b4bcSDuje Mihanović 6*3422b4bcSDuje Mihanović #include <linux/bits.h> 7*3422b4bcSDuje Mihanović #include <linux/bug.h> 8*3422b4bcSDuje Mihanović #include <linux/delay.h> 9*3422b4bcSDuje Mihanović #include <linux/device.h> 10*3422b4bcSDuje Mihanović #include <linux/err.h> 11*3422b4bcSDuje Mihanović #include <linux/i2c.h> 12*3422b4bcSDuje Mihanović #include <linux/math.h> 13*3422b4bcSDuje Mihanović #include <linux/mod_devicetable.h> 14*3422b4bcSDuje Mihanović #include <linux/module.h> 15*3422b4bcSDuje Mihanović #include <linux/platform_device.h> 16*3422b4bcSDuje Mihanović #include <linux/pm_runtime.h> 17*3422b4bcSDuje Mihanović #include <linux/regmap.h> 18*3422b4bcSDuje Mihanović #include <linux/types.h> 19*3422b4bcSDuje Mihanović #include <linux/units.h> 20*3422b4bcSDuje Mihanović 21*3422b4bcSDuje Mihanović #include <asm/byteorder.h> 22*3422b4bcSDuje Mihanović 23*3422b4bcSDuje Mihanović #include <linux/iio/iio.h> 24*3422b4bcSDuje Mihanović #include <linux/iio/types.h> 25*3422b4bcSDuje Mihanović 26*3422b4bcSDuje Mihanović #include <linux/mfd/88pm886.h> 27*3422b4bcSDuje Mihanović 28*3422b4bcSDuje Mihanović struct pm886_gpadc { 29*3422b4bcSDuje Mihanović struct regmap *map; 30*3422b4bcSDuje Mihanović }; 31*3422b4bcSDuje Mihanović 32*3422b4bcSDuje Mihanović enum pm886_gpadc_channel { 33*3422b4bcSDuje Mihanović VSC_CHAN, 34*3422b4bcSDuje Mihanović VCHG_PWR_CHAN, 35*3422b4bcSDuje Mihanović VCF_OUT_CHAN, 36*3422b4bcSDuje Mihanović VBAT_CHAN, 37*3422b4bcSDuje Mihanović VBAT_SLP_CHAN, 38*3422b4bcSDuje Mihanović VBUS_CHAN, 39*3422b4bcSDuje Mihanović 40*3422b4bcSDuje Mihanović GPADC0_CHAN, 41*3422b4bcSDuje Mihanović GPADC1_CHAN, 42*3422b4bcSDuje Mihanović GPADC2_CHAN, 43*3422b4bcSDuje Mihanović GPADC3_CHAN, 44*3422b4bcSDuje Mihanović 45*3422b4bcSDuje Mihanović GND_DET1_CHAN, 46*3422b4bcSDuje Mihanović GND_DET2_CHAN, 47*3422b4bcSDuje Mihanović MIC_DET_CHAN, 48*3422b4bcSDuje Mihanović 49*3422b4bcSDuje Mihanović TINT_CHAN, 50*3422b4bcSDuje Mihanović }; 51*3422b4bcSDuje Mihanović 52*3422b4bcSDuje Mihanović static const int pm886_gpadc_regs[] = { 53*3422b4bcSDuje Mihanović [VSC_CHAN] = PM886_REG_GPADC_VSC, 54*3422b4bcSDuje Mihanović [VCHG_PWR_CHAN] = PM886_REG_GPADC_VCHG_PWR, 55*3422b4bcSDuje Mihanović [VCF_OUT_CHAN] = PM886_REG_GPADC_VCF_OUT, 56*3422b4bcSDuje Mihanović [VBAT_CHAN] = PM886_REG_GPADC_VBAT, 57*3422b4bcSDuje Mihanović [VBAT_SLP_CHAN] = PM886_REG_GPADC_VBAT_SLP, 58*3422b4bcSDuje Mihanović [VBUS_CHAN] = PM886_REG_GPADC_VBUS, 59*3422b4bcSDuje Mihanović 60*3422b4bcSDuje Mihanović [GPADC0_CHAN] = PM886_REG_GPADC_GPADC0, 61*3422b4bcSDuje Mihanović [GPADC1_CHAN] = PM886_REG_GPADC_GPADC1, 62*3422b4bcSDuje Mihanović [GPADC2_CHAN] = PM886_REG_GPADC_GPADC2, 63*3422b4bcSDuje Mihanović [GPADC3_CHAN] = PM886_REG_GPADC_GPADC3, 64*3422b4bcSDuje Mihanović 65*3422b4bcSDuje Mihanović [GND_DET1_CHAN] = PM886_REG_GPADC_GND_DET1, 66*3422b4bcSDuje Mihanović [GND_DET2_CHAN] = PM886_REG_GPADC_GND_DET2, 67*3422b4bcSDuje Mihanović [MIC_DET_CHAN] = PM886_REG_GPADC_MIC_DET, 68*3422b4bcSDuje Mihanović 69*3422b4bcSDuje Mihanović [TINT_CHAN] = PM886_REG_GPADC_TINT, 70*3422b4bcSDuje Mihanović }; 71*3422b4bcSDuje Mihanović 72*3422b4bcSDuje Mihanović #define ADC_CHANNEL_VOLTAGE(index, lsb, name) \ 73*3422b4bcSDuje Mihanović { \ 74*3422b4bcSDuje Mihanović .type = IIO_VOLTAGE, \ 75*3422b4bcSDuje Mihanović .indexed = 1, \ 76*3422b4bcSDuje Mihanović .channel = index, \ 77*3422b4bcSDuje Mihanović .address = lsb, \ 78*3422b4bcSDuje Mihanović .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 79*3422b4bcSDuje Mihanović BIT(IIO_CHAN_INFO_SCALE), \ 80*3422b4bcSDuje Mihanović .datasheet_name = name, \ 81*3422b4bcSDuje Mihanović } 82*3422b4bcSDuje Mihanović 83*3422b4bcSDuje Mihanović #define ADC_CHANNEL_RESISTANCE(index, lsb, name) \ 84*3422b4bcSDuje Mihanović { \ 85*3422b4bcSDuje Mihanović .type = IIO_RESISTANCE, \ 86*3422b4bcSDuje Mihanović .indexed = 1, \ 87*3422b4bcSDuje Mihanović .channel = index, \ 88*3422b4bcSDuje Mihanović .address = lsb, \ 89*3422b4bcSDuje Mihanović .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ 90*3422b4bcSDuje Mihanović .datasheet_name = name, \ 91*3422b4bcSDuje Mihanović } 92*3422b4bcSDuje Mihanović 93*3422b4bcSDuje Mihanović #define ADC_CHANNEL_TEMPERATURE(index, lsb, name) \ 94*3422b4bcSDuje Mihanović { \ 95*3422b4bcSDuje Mihanović .type = IIO_TEMP, \ 96*3422b4bcSDuje Mihanović .indexed = 1, \ 97*3422b4bcSDuje Mihanović .channel = index, \ 98*3422b4bcSDuje Mihanović .address = lsb, \ 99*3422b4bcSDuje Mihanović .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 100*3422b4bcSDuje Mihanović BIT(IIO_CHAN_INFO_SCALE) | \ 101*3422b4bcSDuje Mihanović BIT(IIO_CHAN_INFO_OFFSET), \ 102*3422b4bcSDuje Mihanović .datasheet_name = name, \ 103*3422b4bcSDuje Mihanović } 104*3422b4bcSDuje Mihanović 105*3422b4bcSDuje Mihanović static const struct iio_chan_spec pm886_gpadc_channels[] = { 106*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VSC_CHAN, 1367, "vsc"), 107*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VCHG_PWR_CHAN, 1709, "vchg_pwr"), 108*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VCF_OUT_CHAN, 1367, "vcf_out"), 109*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VBAT_CHAN, 1367, "vbat"), 110*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VBAT_SLP_CHAN, 1367, "vbat_slp"), 111*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(VBUS_CHAN, 1709, "vbus"), 112*3422b4bcSDuje Mihanović 113*3422b4bcSDuje Mihanović ADC_CHANNEL_RESISTANCE(GPADC0_CHAN, 342, "gpadc0"), 114*3422b4bcSDuje Mihanović ADC_CHANNEL_RESISTANCE(GPADC1_CHAN, 342, "gpadc1"), 115*3422b4bcSDuje Mihanović ADC_CHANNEL_RESISTANCE(GPADC2_CHAN, 342, "gpadc2"), 116*3422b4bcSDuje Mihanović ADC_CHANNEL_RESISTANCE(GPADC3_CHAN, 342, "gpadc3"), 117*3422b4bcSDuje Mihanović 118*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(GND_DET1_CHAN, 342, "gnddet1"), 119*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(GND_DET2_CHAN, 342, "gnddet2"), 120*3422b4bcSDuje Mihanović ADC_CHANNEL_VOLTAGE(MIC_DET_CHAN, 1367, "mic_det"), 121*3422b4bcSDuje Mihanović 122*3422b4bcSDuje Mihanović ADC_CHANNEL_TEMPERATURE(TINT_CHAN, 104, "tint"), 123*3422b4bcSDuje Mihanović }; 124*3422b4bcSDuje Mihanović 125*3422b4bcSDuje Mihanović static const struct regmap_config pm886_gpadc_regmap_config = { 126*3422b4bcSDuje Mihanović .reg_bits = 8, 127*3422b4bcSDuje Mihanović .val_bits = 8, 128*3422b4bcSDuje Mihanović .max_register = PM886_GPADC_MAX_REGISTER, 129*3422b4bcSDuje Mihanović }; 130*3422b4bcSDuje Mihanović 131*3422b4bcSDuje Mihanović static int gpadc_get_raw(struct iio_dev *iio, enum pm886_gpadc_channel chan) 132*3422b4bcSDuje Mihanović { 133*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc = iio_priv(iio); 134*3422b4bcSDuje Mihanović __be16 buf; 135*3422b4bcSDuje Mihanović int ret; 136*3422b4bcSDuje Mihanović 137*3422b4bcSDuje Mihanović ret = regmap_bulk_read(gpadc->map, pm886_gpadc_regs[chan], &buf, sizeof(buf)); 138*3422b4bcSDuje Mihanović if (ret) 139*3422b4bcSDuje Mihanović return ret; 140*3422b4bcSDuje Mihanović 141*3422b4bcSDuje Mihanović return be16_to_cpu(buf) >> 4; 142*3422b4bcSDuje Mihanović } 143*3422b4bcSDuje Mihanović 144*3422b4bcSDuje Mihanović static int 145*3422b4bcSDuje Mihanović gpadc_set_bias(struct pm886_gpadc *gpadc, enum pm886_gpadc_channel chan, bool on) 146*3422b4bcSDuje Mihanović { 147*3422b4bcSDuje Mihanović unsigned int gpadc_num = chan - GPADC0_CHAN; 148*3422b4bcSDuje Mihanović unsigned int bits = BIT(gpadc_num + 4) | BIT(gpadc_num); 149*3422b4bcSDuje Mihanović 150*3422b4bcSDuje Mihanović return regmap_assign_bits(gpadc->map, PM886_REG_GPADC_CONFIG(0x14), bits, on); 151*3422b4bcSDuje Mihanović } 152*3422b4bcSDuje Mihanović 153*3422b4bcSDuje Mihanović static int 154*3422b4bcSDuje Mihanović gpadc_find_bias_current(struct iio_dev *iio, struct iio_chan_spec const *chan, 155*3422b4bcSDuje Mihanović unsigned int *raw_uV, unsigned int *raw_uA) 156*3422b4bcSDuje Mihanović { 157*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc = iio_priv(iio); 158*3422b4bcSDuje Mihanović unsigned int gpadc_num = chan->channel - GPADC0_CHAN; 159*3422b4bcSDuje Mihanović unsigned int reg = PM886_REG_GPADC_CONFIG(0xb + gpadc_num); 160*3422b4bcSDuje Mihanović unsigned long lsb = chan->address; 161*3422b4bcSDuje Mihanović int ret; 162*3422b4bcSDuje Mihanović 163*3422b4bcSDuje Mihanović for (unsigned int i = 0; i < PM886_GPADC_BIAS_LEVELS; i++) { 164*3422b4bcSDuje Mihanović ret = regmap_update_bits(gpadc->map, reg, GENMASK(3, 0), i); 165*3422b4bcSDuje Mihanović if (ret) 166*3422b4bcSDuje Mihanović return ret; 167*3422b4bcSDuje Mihanović 168*3422b4bcSDuje Mihanović /* Wait for the new bias level to apply. */ 169*3422b4bcSDuje Mihanović fsleep(5 * USEC_PER_MSEC); 170*3422b4bcSDuje Mihanović 171*3422b4bcSDuje Mihanović *raw_uA = PM886_GPADC_INDEX_TO_BIAS_uA(i); 172*3422b4bcSDuje Mihanović *raw_uV = gpadc_get_raw(iio, chan->channel) * lsb; 173*3422b4bcSDuje Mihanović 174*3422b4bcSDuje Mihanović /* 175*3422b4bcSDuje Mihanović * Vendor kernel errors out above 1.25 V, but testing shows 176*3422b4bcSDuje Mihanović * that the resistance of the battery detection channel (GPADC2 177*3422b4bcSDuje Mihanović * on coreprimevelte) reaches about 1.4 MΩ when the battery is 178*3422b4bcSDuje Mihanović * removed, which can't be measured with such a low upper 179*3422b4bcSDuje Mihanović * limit. Therefore, to be able to detect the battery without 180*3422b4bcSDuje Mihanović * ugly externs as used in the vendor fuel gauge driver, 181*3422b4bcSDuje Mihanović * increase this limit a bit. 182*3422b4bcSDuje Mihanović */ 183*3422b4bcSDuje Mihanović if (WARN_ON(*raw_uV > 1500 * (MICRO / MILLI))) 184*3422b4bcSDuje Mihanović return -EIO; 185*3422b4bcSDuje Mihanović 186*3422b4bcSDuje Mihanović /* 187*3422b4bcSDuje Mihanović * Vendor kernel errors out under 300 mV, but for the same 188*3422b4bcSDuje Mihanović * reason as above (except the channel hovers around 3.5 kΩ 189*3422b4bcSDuje Mihanović * with battery present) reduce this limit. 190*3422b4bcSDuje Mihanović */ 191*3422b4bcSDuje Mihanović if (*raw_uV < 200 * (MICRO / MILLI)) { 192*3422b4bcSDuje Mihanović dev_dbg(&iio->dev, "bad bias for chan %d: %d uA @ %d uV\n", 193*3422b4bcSDuje Mihanović chan->channel, *raw_uA, *raw_uV); 194*3422b4bcSDuje Mihanović continue; 195*3422b4bcSDuje Mihanović } 196*3422b4bcSDuje Mihanović 197*3422b4bcSDuje Mihanović dev_dbg(&iio->dev, "good bias for chan %d: %d uA @ %d uV\n", 198*3422b4bcSDuje Mihanović chan->channel, *raw_uA, *raw_uV); 199*3422b4bcSDuje Mihanović return 0; 200*3422b4bcSDuje Mihanović } 201*3422b4bcSDuje Mihanović 202*3422b4bcSDuje Mihanović dev_err(&iio->dev, "failed to find good bias for chan %d\n", chan->channel); 203*3422b4bcSDuje Mihanović return -EINVAL; 204*3422b4bcSDuje Mihanović } 205*3422b4bcSDuje Mihanović 206*3422b4bcSDuje Mihanović static int 207*3422b4bcSDuje Mihanović gpadc_get_resistance_ohm(struct iio_dev *iio, struct iio_chan_spec const *chan) 208*3422b4bcSDuje Mihanović { 209*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc = iio_priv(iio); 210*3422b4bcSDuje Mihanović unsigned int raw_uV, raw_uA; 211*3422b4bcSDuje Mihanović int ret; 212*3422b4bcSDuje Mihanović 213*3422b4bcSDuje Mihanović ret = gpadc_set_bias(gpadc, chan->channel, true); 214*3422b4bcSDuje Mihanović if (ret) 215*3422b4bcSDuje Mihanović goto out; 216*3422b4bcSDuje Mihanović 217*3422b4bcSDuje Mihanović ret = gpadc_find_bias_current(iio, chan, &raw_uV, &raw_uA); 218*3422b4bcSDuje Mihanović if (ret) 219*3422b4bcSDuje Mihanović goto out; 220*3422b4bcSDuje Mihanović 221*3422b4bcSDuje Mihanović ret = DIV_ROUND_CLOSEST(raw_uV, raw_uA); 222*3422b4bcSDuje Mihanović out: 223*3422b4bcSDuje Mihanović gpadc_set_bias(gpadc, chan->channel, false); 224*3422b4bcSDuje Mihanović return ret; 225*3422b4bcSDuje Mihanović } 226*3422b4bcSDuje Mihanović 227*3422b4bcSDuje Mihanović static int 228*3422b4bcSDuje Mihanović __pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, 229*3422b4bcSDuje Mihanović int *val, int *val2, long mask) 230*3422b4bcSDuje Mihanović { 231*3422b4bcSDuje Mihanović unsigned long lsb = chan->address; 232*3422b4bcSDuje Mihanović 233*3422b4bcSDuje Mihanović switch (mask) { 234*3422b4bcSDuje Mihanović case IIO_CHAN_INFO_RAW: 235*3422b4bcSDuje Mihanović *val = gpadc_get_raw(iio, chan->channel); 236*3422b4bcSDuje Mihanović if (*val < 0) 237*3422b4bcSDuje Mihanović return *val; 238*3422b4bcSDuje Mihanović 239*3422b4bcSDuje Mihanović return IIO_VAL_INT; 240*3422b4bcSDuje Mihanović case IIO_CHAN_INFO_SCALE: 241*3422b4bcSDuje Mihanović *val = lsb; 242*3422b4bcSDuje Mihanović 243*3422b4bcSDuje Mihanović if (chan->type == IIO_VOLTAGE) { 244*3422b4bcSDuje Mihanović *val2 = MILLI; 245*3422b4bcSDuje Mihanović return IIO_VAL_FRACTIONAL; 246*3422b4bcSDuje Mihanović } else { 247*3422b4bcSDuje Mihanović return IIO_VAL_INT; 248*3422b4bcSDuje Mihanović } 249*3422b4bcSDuje Mihanović case IIO_CHAN_INFO_OFFSET: 250*3422b4bcSDuje Mihanović /* Raw value is 104 millikelvin/LSB, convert it to 104 millicelsius/LSB */ 251*3422b4bcSDuje Mihanović *val = ABSOLUTE_ZERO_MILLICELSIUS; 252*3422b4bcSDuje Mihanović *val2 = lsb; 253*3422b4bcSDuje Mihanović return IIO_VAL_FRACTIONAL; 254*3422b4bcSDuje Mihanović case IIO_CHAN_INFO_PROCESSED: 255*3422b4bcSDuje Mihanović *val = gpadc_get_resistance_ohm(iio, chan); 256*3422b4bcSDuje Mihanović if (*val < 0) 257*3422b4bcSDuje Mihanović return *val; 258*3422b4bcSDuje Mihanović 259*3422b4bcSDuje Mihanović return IIO_VAL_INT; 260*3422b4bcSDuje Mihanović default: 261*3422b4bcSDuje Mihanović return -EINVAL; 262*3422b4bcSDuje Mihanović } 263*3422b4bcSDuje Mihanović } 264*3422b4bcSDuje Mihanović 265*3422b4bcSDuje Mihanović static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, 266*3422b4bcSDuje Mihanović int *val, int *val2, long mask) 267*3422b4bcSDuje Mihanović { 268*3422b4bcSDuje Mihanović struct device *dev = iio->dev.parent; 269*3422b4bcSDuje Mihanović int ret; 270*3422b4bcSDuje Mihanović 271*3422b4bcSDuje Mihanović ret = pm_runtime_resume_and_get(dev); 272*3422b4bcSDuje Mihanović if (ret) 273*3422b4bcSDuje Mihanović return ret; 274*3422b4bcSDuje Mihanović 275*3422b4bcSDuje Mihanović ret = __pm886_gpadc_read_raw(iio, chan, val, val2, mask); 276*3422b4bcSDuje Mihanović 277*3422b4bcSDuje Mihanović pm_runtime_put_autosuspend(dev); 278*3422b4bcSDuje Mihanović return ret; 279*3422b4bcSDuje Mihanović } 280*3422b4bcSDuje Mihanović 281*3422b4bcSDuje Mihanović static int pm886_gpadc_hw_enable(struct regmap *map) 282*3422b4bcSDuje Mihanović { 283*3422b4bcSDuje Mihanović const u8 config[] = { 284*3422b4bcSDuje Mihanović PM886_GPADC_CONFIG1_EN_ALL, 285*3422b4bcSDuje Mihanović PM886_GPADC_CONFIG2_EN_ALL, 286*3422b4bcSDuje Mihanović PM886_GPADC_GND_DET2_EN, 287*3422b4bcSDuje Mihanović }; 288*3422b4bcSDuje Mihanović int ret; 289*3422b4bcSDuje Mihanović 290*3422b4bcSDuje Mihanović /* Enable the ADC block. */ 291*3422b4bcSDuje Mihanović ret = regmap_set_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0)); 292*3422b4bcSDuje Mihanović if (ret) 293*3422b4bcSDuje Mihanović return ret; 294*3422b4bcSDuje Mihanović 295*3422b4bcSDuje Mihanović /* Enable all channels. */ 296*3422b4bcSDuje Mihanović return regmap_bulk_write(map, PM886_REG_GPADC_CONFIG(0x1), config, ARRAY_SIZE(config)); 297*3422b4bcSDuje Mihanović } 298*3422b4bcSDuje Mihanović 299*3422b4bcSDuje Mihanović static int pm886_gpadc_hw_disable(struct regmap *map) 300*3422b4bcSDuje Mihanović { 301*3422b4bcSDuje Mihanović return regmap_clear_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0)); 302*3422b4bcSDuje Mihanović } 303*3422b4bcSDuje Mihanović 304*3422b4bcSDuje Mihanović static const struct iio_info pm886_gpadc_iio_info = { 305*3422b4bcSDuje Mihanović .read_raw = pm886_gpadc_read_raw, 306*3422b4bcSDuje Mihanović }; 307*3422b4bcSDuje Mihanović 308*3422b4bcSDuje Mihanović static int pm886_gpadc_probe(struct platform_device *pdev) 309*3422b4bcSDuje Mihanović { 310*3422b4bcSDuje Mihanović struct device *dev = &pdev->dev; 311*3422b4bcSDuje Mihanović struct pm886_chip *chip = dev_get_drvdata(dev->parent); 312*3422b4bcSDuje Mihanović struct i2c_client *client = chip->client; 313*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc; 314*3422b4bcSDuje Mihanović struct i2c_client *page; 315*3422b4bcSDuje Mihanović struct iio_dev *iio; 316*3422b4bcSDuje Mihanović int ret; 317*3422b4bcSDuje Mihanović 318*3422b4bcSDuje Mihanović iio = devm_iio_device_alloc(dev, sizeof(*gpadc)); 319*3422b4bcSDuje Mihanović if (!iio) 320*3422b4bcSDuje Mihanović return -ENOMEM; 321*3422b4bcSDuje Mihanović 322*3422b4bcSDuje Mihanović gpadc = iio_priv(iio); 323*3422b4bcSDuje Mihanović dev_set_drvdata(dev, iio); 324*3422b4bcSDuje Mihanović 325*3422b4bcSDuje Mihanović page = devm_i2c_new_dummy_device(dev, client->adapter, 326*3422b4bcSDuje Mihanović client->addr + PM886_PAGE_OFFSET_GPADC); 327*3422b4bcSDuje Mihanović if (IS_ERR(page)) 328*3422b4bcSDuje Mihanović return dev_err_probe(dev, PTR_ERR(page), "Failed to initialize GPADC page\n"); 329*3422b4bcSDuje Mihanović 330*3422b4bcSDuje Mihanović gpadc->map = devm_regmap_init_i2c(page, &pm886_gpadc_regmap_config); 331*3422b4bcSDuje Mihanović if (IS_ERR(gpadc->map)) 332*3422b4bcSDuje Mihanović return dev_err_probe(dev, PTR_ERR(gpadc->map), 333*3422b4bcSDuje Mihanović "Failed to initialize GPADC regmap\n"); 334*3422b4bcSDuje Mihanović 335*3422b4bcSDuje Mihanović iio->name = "88pm886-gpadc"; 336*3422b4bcSDuje Mihanović iio->modes = INDIO_DIRECT_MODE; 337*3422b4bcSDuje Mihanović iio->info = &pm886_gpadc_iio_info; 338*3422b4bcSDuje Mihanović iio->channels = pm886_gpadc_channels; 339*3422b4bcSDuje Mihanović iio->num_channels = ARRAY_SIZE(pm886_gpadc_channels); 340*3422b4bcSDuje Mihanović device_set_node(&iio->dev, dev_fwnode(dev->parent)); 341*3422b4bcSDuje Mihanović 342*3422b4bcSDuje Mihanović ret = devm_pm_runtime_enable(dev); 343*3422b4bcSDuje Mihanović if (ret) 344*3422b4bcSDuje Mihanović return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); 345*3422b4bcSDuje Mihanović 346*3422b4bcSDuje Mihanović pm_runtime_set_autosuspend_delay(dev, 50); 347*3422b4bcSDuje Mihanović pm_runtime_use_autosuspend(dev); 348*3422b4bcSDuje Mihanović ret = devm_iio_device_register(dev, iio); 349*3422b4bcSDuje Mihanović if (ret) 350*3422b4bcSDuje Mihanović return dev_err_probe(dev, ret, "Failed to register ADC\n"); 351*3422b4bcSDuje Mihanović 352*3422b4bcSDuje Mihanović return 0; 353*3422b4bcSDuje Mihanović } 354*3422b4bcSDuje Mihanović 355*3422b4bcSDuje Mihanović static int pm886_gpadc_runtime_resume(struct device *dev) 356*3422b4bcSDuje Mihanović { 357*3422b4bcSDuje Mihanović struct iio_dev *iio = dev_get_drvdata(dev); 358*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc = iio_priv(iio); 359*3422b4bcSDuje Mihanović 360*3422b4bcSDuje Mihanović return pm886_gpadc_hw_enable(gpadc->map); 361*3422b4bcSDuje Mihanović } 362*3422b4bcSDuje Mihanović 363*3422b4bcSDuje Mihanović static int pm886_gpadc_runtime_suspend(struct device *dev) 364*3422b4bcSDuje Mihanović { 365*3422b4bcSDuje Mihanović struct iio_dev *iio = dev_get_drvdata(dev); 366*3422b4bcSDuje Mihanović struct pm886_gpadc *gpadc = iio_priv(iio); 367*3422b4bcSDuje Mihanović 368*3422b4bcSDuje Mihanović return pm886_gpadc_hw_disable(gpadc->map); 369*3422b4bcSDuje Mihanović } 370*3422b4bcSDuje Mihanović 371*3422b4bcSDuje Mihanović static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops, 372*3422b4bcSDuje Mihanović pm886_gpadc_runtime_suspend, 373*3422b4bcSDuje Mihanović pm886_gpadc_runtime_resume, NULL); 374*3422b4bcSDuje Mihanović 375*3422b4bcSDuje Mihanović static const struct platform_device_id pm886_gpadc_id[] = { 376*3422b4bcSDuje Mihanović { "88pm886-gpadc" }, 377*3422b4bcSDuje Mihanović { } 378*3422b4bcSDuje Mihanović }; 379*3422b4bcSDuje Mihanović MODULE_DEVICE_TABLE(platform, pm886_gpadc_id); 380*3422b4bcSDuje Mihanović 381*3422b4bcSDuje Mihanović static struct platform_driver pm886_gpadc_driver = { 382*3422b4bcSDuje Mihanović .driver = { 383*3422b4bcSDuje Mihanović .name = "88pm886-gpadc", 384*3422b4bcSDuje Mihanović .pm = pm_ptr(&pm886_gpadc_pm_ops), 385*3422b4bcSDuje Mihanović }, 386*3422b4bcSDuje Mihanović .probe = pm886_gpadc_probe, 387*3422b4bcSDuje Mihanović .id_table = pm886_gpadc_id, 388*3422b4bcSDuje Mihanović }; 389*3422b4bcSDuje Mihanović module_platform_driver(pm886_gpadc_driver); 390*3422b4bcSDuje Mihanović 391*3422b4bcSDuje Mihanović MODULE_AUTHOR("Duje Mihanović <duje@dujemihanovic.xyz>"); 392*3422b4bcSDuje Mihanović MODULE_DESCRIPTION("Marvell 88PM886 GPADC driver"); 393*3422b4bcSDuje Mihanović MODULE_LICENSE("GPL"); 394