Duje Mihanović, 2025-08-31T12:33:05+02:00:
> Marvell's 88PM886 PMIC has a so-called General Purpose ADC used for
> monitoring various system voltages and temperatures. Add the relevant
> register definitions to the MFD header and a driver for the ADC.
>
> Signed-off-by: Duje Mihanović <d...@dujemihanovic.xyz>
> ---
> v2:
> - default MFD_88PM886_PMIC
> - u8[2] -> __be16
> - Drop kernel.h include
> - Add pm886_gpadc struct
> - Reorder channel enum
> - Drop GPADC voltage channels
> - Drop unnecessary masking in gpadc_get_raw()
> - Extend gpadc_enable_bias() to allow disabling bias
> - usleep_range() -> fsleep()
> - PM wrapper for pm886_gpadc_read_raw()
> - Proper channel info: voltage is RAW | SCALE, temperature is RAW |
>   OFFSET | SCALE, resistance is PROCESSED
> - Explicitly define channels to en/disable in pm886_gpadc_setup()
> - Don't explicitly set iio->dev.parent
> - Miscellaneous style changes
> ---
>  MAINTAINERS                     |   5 +
>  drivers/iio/adc/88pm886-gpadc.c | 383 
> ++++++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/Kconfig         |  13 ++
>  drivers/iio/adc/Makefile        |   1 +
>  include/linux/mfd/88pm886.h     |  54 ++++++
>  5 files changed, 456 insertions(+)
>

[...]

> diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c
> new file mode 100644
> index 
> 0000000000000000000000000000000000000000..4622d2525e0edeed89c6e6d43336b177590aa885
> --- /dev/null
> +++ b/drivers/iio/adc/88pm886-gpadc.c

[...]

> +static int pm886_gpadc_probe(struct platform_device *pdev)
> +{
> +     struct device *dev = &pdev->dev;
> +     struct pm886_chip *chip = dev_get_drvdata(dev->parent);
> +     struct i2c_client *client = chip->client;
> +     struct pm886_gpadc *gpadc;
> +     struct i2c_client *page;
> +     struct iio_dev *iio;
> +     int ret;
> +
> +     iio = devm_iio_device_alloc(dev, sizeof(*gpadc));
> +     if (!iio)
> +             return -ENOMEM;
> +
> +     gpadc = iio_priv(iio);
> +     dev_set_drvdata(dev, iio);
> +
> +     page = devm_i2c_new_dummy_device(dev, client->adapter,
> +                                      client->addr + 
> PM886_PAGE_OFFSET_GPADC);
> +     if (IS_ERR(page))
> +             return dev_err_probe(dev, PTR_ERR(page), "Failed to initialize 
> GPADC page\n");
> +
> +     gpadc->map = devm_regmap_init_i2c(page, &pm886_gpadc_regmap_config);
> +     if (IS_ERR(gpadc->map))
> +             return dev_err_probe(dev, PTR_ERR(gpadc->map),
> +                                  "Failed to initialize GPADC regmap\n");
> +
> +     iio->name = "88pm886-gpadc";
> +     iio->dev.of_node = dev->parent->of_node;

Didn't you mean to drop this since Jonathan pointed out that it's done
by the core?

> +     iio->modes = INDIO_DIRECT_MODE;
> +     iio->info = &pm886_gpadc_iio_info;
> +     iio->channels = pm886_gpadc_channels;
> +     iio->num_channels = ARRAY_SIZE(pm886_gpadc_channels);
> +
> +     pm_runtime_set_autosuspend_delay(dev, 50);
> +     pm_runtime_use_autosuspend(dev);
> +     ret = devm_pm_runtime_enable(dev);
> +     if (ret)
> +             return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
> +
> +     ret = devm_iio_device_register(dev, iio);
> +     if (ret)
> +             return dev_err_probe(dev, ret, "Failed to register ADC\n");
> +
> +     return 0;
> +}

[...]

> diff --git a/include/linux/mfd/88pm886.h b/include/linux/mfd/88pm886.h
> index 
> 85eca44f39ab58ba4cb9ec4216118ee9604d021f..85c3c16fb10b7ee6aafdd6e68fd9135d8009eef8
>  100644
> --- a/include/linux/mfd/88pm886.h
> +++ b/include/linux/mfd/88pm886.h
> @@ -10,6 +10,7 @@
>  #define PM886_IRQ_ONKEY                      0
>  
>  #define PM886_PAGE_OFFSET_REGULATORS 1
> +#define PM886_PAGE_OFFSET_GPADC              2
>  
>  #define PM886_REG_ID                 0x00
>  
> @@ -67,6 +68,59 @@
>  #define PM886_REG_BUCK4_VOUT         0xcf
>  #define PM886_REG_BUCK5_VOUT         0xdd
>  
> +/* GPADC enable/disable registers */
> +#define PM886_REG_GPADC_CONFIG(n)    (n)
> +
> +/* GPADC channel registers */
> +#define PM886_REG_GPADC_VSC          0x40
> +#define PM886_REG_GPADC_VCHG_PWR     0x4c
> +#define PM886_REG_GPADC_VCF_OUT              0x4e
> +#define PM886_REG_GPADC_TINT         0x50
> +#define PM886_REG_GPADC_GPADC0               0x54
> +#define PM886_REG_GPADC_GPADC1               0x56
> +#define PM886_REG_GPADC_GPADC2               0x58
> +#define PM886_REG_GPADC_VBAT         0xa0
> +#define PM886_REG_GPADC_GND_DET1     0xa4
> +#define PM886_REG_GPADC_GND_DET2     0xa6
> +#define PM886_REG_GPADC_VBUS         0xa8
> +#define PM886_REG_GPADC_GPADC3               0xaa
> +#define PM886_REG_GPADC_MIC_DET              0xac
> +#define PM886_REG_GPADC_VBAT_SLP     0xb0
> +
> +/* GPADC channel enable bits */
> +#define PM886_GPADC_VSC_EN   BIT(0)
> +#define PM886_GPADC_VBAT_EN  BIT(1)
> +#define PM886_GPADC_GNDDET1_EN       BIT(3)
> +#define PM886_GPADC_VBUS_EN  BIT(4)
> +#define PM886_GPADC_VCHG_PWR_EN      BIT(5)
> +#define PM886_GPADC_VCF_OUT_EN       BIT(6)
> +#define PM886_GPADC_CONFIG1_EN_ALL (PM886_GPADC_VSC_EN | \
> +                                 PM886_GPADC_VBAT_EN | \
> +                                 PM886_GPADC_GNDDET1_EN | \
> +                                 PM886_GPADC_VBUS_EN | \
> +                                 PM886_GPADC_VCHG_PWR_EN | \
> +                                 PM886_GPADC_VCF_OUT_EN)
> +
> +#define PM886_GPADC_TINT_EN  BIT(0)
> +#define PM886_GPADC_PMODE_EN BIT(1)
> +#define PM886_GPADC_GPADC0_EN        BIT(2)
> +#define PM886_GPADC_GPADC1_EN        BIT(3)
> +#define PM886_GPADC_GPADC2_EN        BIT(4)
> +#define PM886_GPADC_GPADC3_EN        BIT(5)
> +#define PM886_GPADC_MIC_DET_EN       BIT(6)
> +#define PM886_GPADC_CONFIG2_EN_ALL (PM886_GPADC_TINT_EN | \
> +                                 PM886_GPADC_GPADC0_EN | \
> +                                 PM886_GPADC_GPADC1_EN | \
> +                                 PM886_GPADC_GPADC2_EN | \
> +                                 PM886_GPADC_GPADC3_EN | \
> +                                 PM886_GPADC_MIC_DET_EN)
> +
> +/* No CONFIG3_EN_ALL because this is the only bit there */
> +#define PM886_GPADC_GND_DET2_EN      BIT(0)

I have previously ordered the definitions here to always have the
individual bits follow the register definition (see above REG_STATUS1
for instance). I don't know if this is a common practice, but it seemed
useful to me to make it clear which register the bits are found in
without having to look at their usage (even though in your case it is
clear from the _EN_ALL definition following them) and without making
them long by stuffing the register name in their names. So if you wanted
to follow this logic, the preceding three paragraphs should be moved
after the GPADC_CONFIG macro (since you used that to condense the
individual register definitions).

Also a nit, the above comment is I believe a full sentence, so it should
have a period at the end (I wouldn't mention it, but I seem to recall
that Lee was keen on this :-).

> +
> +#define PM886_GPADC_BIAS_LEVELS              16
> +#define PM886_GPADC_INDEX_TO_BIAS_UA(i)      (1 + (i) * 5)
> +
>  #define PM886_LDO_VSEL_MASK          0x0f
>  #define PM886_BUCK_VSEL_MASK         0x7f

It would also seem logical to me to keep these last two grouped with the
other regulators-related definitions.

Reply via email to