mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
1fd4c0cd78
Do not trust the fact that iio.h includes of.h which in turn includes all the headers we are relying on. The ultimate goal is to actually drop of.h from iio.h. Signed-off-by: Nuno Sá <nuno.sa@analog.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Link: https://lore.kernel.org/r/20220610084545.547700-8-nuno.sa@analog.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
209 lines
4.9 KiB
C
209 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* MP2629 Driver for ADC
|
|
*
|
|
* Copyright 2020 Monolithic Power Systems, Inc
|
|
*
|
|
* Author: Saravanan Sekar <sravanhome@gmail.com>
|
|
*/
|
|
|
|
#include <linux/iio/driver.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/machine.h>
|
|
#include <linux/mfd/mp2629.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#define MP2629_REG_ADC_CTRL 0x03
|
|
#define MP2629_REG_BATT_VOLT 0x0e
|
|
#define MP2629_REG_SYSTEM_VOLT 0x0f
|
|
#define MP2629_REG_INPUT_VOLT 0x11
|
|
#define MP2629_REG_BATT_CURRENT 0x12
|
|
#define MP2629_REG_INPUT_CURRENT 0x13
|
|
|
|
#define MP2629_ADC_START BIT(7)
|
|
#define MP2629_ADC_CONTINUOUS BIT(6)
|
|
|
|
#define MP2629_MAP(_mp, _mpc) IIO_MAP(#_mp, "mp2629_charger", "mp2629-"_mpc)
|
|
|
|
#define MP2629_ADC_CHAN(_ch, _type) { \
|
|
.type = _type, \
|
|
.indexed = 1, \
|
|
.datasheet_name = #_ch, \
|
|
.channel = MP2629_ ## _ch, \
|
|
.address = MP2629_REG_ ## _ch, \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
|
}
|
|
|
|
struct mp2629_adc {
|
|
struct regmap *regmap;
|
|
struct device *dev;
|
|
};
|
|
|
|
static struct iio_chan_spec mp2629_channels[] = {
|
|
MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE),
|
|
MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE),
|
|
MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE),
|
|
MP2629_ADC_CHAN(BATT_CURRENT, IIO_CURRENT),
|
|
MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT)
|
|
};
|
|
|
|
static struct iio_map mp2629_adc_maps[] = {
|
|
MP2629_MAP(BATT_VOLT, "batt-volt"),
|
|
MP2629_MAP(SYSTEM_VOLT, "system-volt"),
|
|
MP2629_MAP(INPUT_VOLT, "input-volt"),
|
|
MP2629_MAP(BATT_CURRENT, "batt-current"),
|
|
MP2629_MAP(INPUT_CURRENT, "input-current")
|
|
};
|
|
|
|
static int mp2629_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct mp2629_adc *info = iio_priv(indio_dev);
|
|
unsigned int rval;
|
|
int ret;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
ret = regmap_read(info->regmap, chan->address, &rval);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (chan->address == MP2629_INPUT_VOLT)
|
|
rval &= GENMASK(6, 0);
|
|
*val = rval;
|
|
return IIO_VAL_INT;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
switch (chan->channel) {
|
|
case MP2629_BATT_VOLT:
|
|
case MP2629_SYSTEM_VOLT:
|
|
*val = 20;
|
|
return IIO_VAL_INT;
|
|
|
|
case MP2629_INPUT_VOLT:
|
|
*val = 60;
|
|
return IIO_VAL_INT;
|
|
|
|
case MP2629_BATT_CURRENT:
|
|
*val = 175;
|
|
*val2 = 10;
|
|
return IIO_VAL_FRACTIONAL;
|
|
|
|
case MP2629_INPUT_CURRENT:
|
|
*val = 133;
|
|
*val2 = 10;
|
|
return IIO_VAL_FRACTIONAL;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static const struct iio_info mp2629_adc_info = {
|
|
.read_raw = &mp2629_read_raw,
|
|
};
|
|
|
|
static int mp2629_adc_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct mp2629_data *ddata = dev_get_drvdata(dev->parent);
|
|
struct mp2629_adc *info;
|
|
struct iio_dev *indio_dev;
|
|
int ret;
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
info = iio_priv(indio_dev);
|
|
info->regmap = ddata->regmap;
|
|
info->dev = dev;
|
|
platform_set_drvdata(pdev, indio_dev);
|
|
|
|
ret = regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
|
|
MP2629_ADC_START | MP2629_ADC_CONTINUOUS,
|
|
MP2629_ADC_START | MP2629_ADC_CONTINUOUS);
|
|
if (ret) {
|
|
dev_err(dev, "adc enable fail: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = iio_map_array_register(indio_dev, mp2629_adc_maps);
|
|
if (ret) {
|
|
dev_err(dev, "IIO maps register fail: %d\n", ret);
|
|
goto fail_disable;
|
|
}
|
|
|
|
indio_dev->name = "mp2629-adc";
|
|
indio_dev->channels = mp2629_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(mp2629_channels);
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->info = &mp2629_adc_info;
|
|
|
|
ret = iio_device_register(indio_dev);
|
|
if (ret) {
|
|
dev_err(dev, "IIO device register fail: %d\n", ret);
|
|
goto fail_map_unregister;
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail_map_unregister:
|
|
iio_map_array_unregister(indio_dev);
|
|
|
|
fail_disable:
|
|
regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
|
|
MP2629_ADC_CONTINUOUS, 0);
|
|
regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
|
|
MP2629_ADC_START, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mp2629_adc_remove(struct platform_device *pdev)
|
|
{
|
|
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
|
struct mp2629_adc *info = iio_priv(indio_dev);
|
|
|
|
iio_device_unregister(indio_dev);
|
|
|
|
iio_map_array_unregister(indio_dev);
|
|
|
|
regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
|
|
MP2629_ADC_CONTINUOUS, 0);
|
|
regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
|
|
MP2629_ADC_START, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id mp2629_adc_of_match[] = {
|
|
{ .compatible = "mps,mp2629_adc"},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, mp2629_adc_of_match);
|
|
|
|
static struct platform_driver mp2629_adc_driver = {
|
|
.driver = {
|
|
.name = "mp2629_adc",
|
|
.of_match_table = mp2629_adc_of_match,
|
|
},
|
|
.probe = mp2629_adc_probe,
|
|
.remove = mp2629_adc_remove,
|
|
};
|
|
module_platform_driver(mp2629_adc_driver);
|
|
|
|
MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
|
|
MODULE_DESCRIPTION("MP2629 ADC driver");
|
|
MODULE_LICENSE("GPL");
|