blob: 5cab464d749bf19f0a32459406a390c673ffd791 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <dt-bindings/mfd/mt6362.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define MT6362_REG_DEVINFO (0x00)
#define MT6362_REG_TMINFO (0x0F)
#define MT6362_REG_ADCCFG1 (0xA4)
#define MT6362_REG_ADCCFG3 (0xA6)
#define MT6362_REG_ADCEN1 (0xA7)
#define MT6362_CHRPT_BASEADDR (0xAA)
#define MT6362_CHG_STAT2 (0xE2)
#define MT6362_CHIPREV_MASK (0x0F)
#define MT6362_CHIPREV_E2 (0x2)
#define MT6362_TMID3_MASK BIT(4)
#define MT6362_STSYSMIN_MASK BIT(1)
#define MT6362_ADCEN_MASK BIT(7)
/* ADC conversion time in microseconds */
#define MT6362_TIME_PERCH (1300)
#define MT6362_LTIME_PERCH (2200)
#define MT6362_DESIRECH_SHIFT (4)
#define MT6362_IRQRPTCH_SHIFT (0)
#define MT6362_VSYSMINST_CNT (3)
#define MT6362_ADCIBAT_OFFSET (100 * 1000)
#define MT6362_ADCSYSMIN_OFFSET (150 * 1000)
#define MT6362_CHRPT_ADDR(_idx) (MT6362_CHRPT_BASEADDR + ((_idx) - 1) * 2)
struct mt6362_adc_data {
struct device *dev;
struct regmap *regmap;
struct mutex adc_lock;
struct completion adc_comp;
bool ibat_offset_flag;
bool conv_ltime_flag;
};
static const int mt6362_adcch_offsets[] = {
0, 0, 0, 0, 0, 0, -64, 0, 0, 0, 0, 0, 0, 0, 0,
};
static const int mt6362_adcch_times[] = {
/* chgvin, vsys, vbat, ibus, ibat, vddp, tempjc*/
6250, 1250, 1250, 2500, 2500, 1250, 1,
/* verfts, ts, pdvbus, cc1, cc2, sbu1, sbu2 */
1250, 1250, 25000, 10300, 10300, 2575, 2575,
/* zcv */
1250,
};
static int mt6362_adc_is_sysmin_state(struct mt6362_adc_data *data, bool *state)
{
unsigned int val = 0;
int i, rv;
*state = true;
for (i = 0; i < MT6362_VSYSMINST_CNT; i++) {
rv = regmap_read(data->regmap, MT6362_CHG_STAT2, &val);
if (rv)
return rv;
/* either one is not in vsys min, treat as VBAT>VSYSMIN */
if (!(val & MT6362_STSYSMIN_MASK)) {
*state = false;
break;
}
}
return 0;
}
static int mt6362_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mt6362_adc_data *data = iio_priv(indio_dev);
u8 oneshot_ch;
u16 raw = 0;
unsigned int conv_time =
data->conv_ltime_flag ? MT6362_LTIME_PERCH : MT6362_TIME_PERCH;
bool state;
int rv;
mutex_lock(&data->adc_lock);
if (chan->scan_index == MT6362_ADCCH_ZCV)
goto direct_read;
/* change ADC_EN=0 to prevent the other channels routine task */
rv = regmap_update_bits(data->regmap,
MT6362_REG_ADCCFG1, MT6362_ADCEN_MASK, 0);
if (rv)
goto out_read;
/* config onshot channel and irq reported channel */
oneshot_ch = chan->channel << MT6362_DESIRECH_SHIFT;
oneshot_ch |= chan->channel << MT6362_IRQRPTCH_SHIFT;
rv = regmap_write(data->regmap, MT6362_REG_ADCCFG3, oneshot_ch);
if (rv)
goto out_read;
/* change ADC_EN=1 to start oneshot channel as the first run */
rv = regmap_update_bits(data->regmap, MT6362_REG_ADCCFG1,
MT6362_ADCEN_MASK, MT6362_ADCEN_MASK);
if (rv)
goto out_read;
reinit_completion(&data->adc_comp);
wait_for_completion_timeout(&data->adc_comp,
usecs_to_jiffies(2 * conv_time));
/* clear oneshot channel and irq report channel */
rv = regmap_write(data->regmap, MT6362_REG_ADCCFG3, 0);
if (rv)
goto out_read;
direct_read:
/* dummy write high byte then read */
rv = regmap_write(data->regmap, chan->address, 0);
if (rv)
goto out_read;
rv = regmap_bulk_read(data->regmap, chan->address, &raw, 2);
if (rv)
goto out_read;
raw = be16_to_cpu(raw);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = raw;
break;
case IIO_CHAN_INFO_PROCESSED:
*val = mt6362_adcch_offsets[chan->scan_index];
*val += (raw * mt6362_adcch_times[chan->scan_index]);
if (chan->scan_index == MT6362_ADCCH_IBAT &&
data->ibat_offset_flag) {
rv = mt6362_adc_is_sysmin_state(data, &state);
if (rv)
goto out_read;
if (state)
*val += MT6362_ADCSYSMIN_OFFSET;
*val -= MT6362_ADCIBAT_OFFSET;
}
break;
default:
rv = -EINVAL;
}
out_read:
mutex_unlock(&data->adc_lock);
return rv ? : IIO_VAL_INT;
}
static const struct iio_info mt6362_adc_info = {
.read_raw = mt6362_adc_read_raw,
};
#define MT6362_ADC_CHAN(_idx, _si, _type) \
{\
.type = (_type), \
.channel = (_idx), \
.address = MT6362_CHRPT_ADDR(_idx), \
.scan_index = MT6362_ADCCH_##_si, \
.scan_type = { \
.sign = 's', \
.realbits = 32, \
.storagebits = 32, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED), \
.datasheet_name = "ADC_CH" #_idx, \
.indexed = 1, \
}
static const struct iio_chan_spec mt6362_adc_channels[] = {
MT6362_ADC_CHAN(1, CHGVINDIV5, IIO_VOLTAGE),
MT6362_ADC_CHAN(3, VSYS, IIO_VOLTAGE),
MT6362_ADC_CHAN(4, VBAT, IIO_VOLTAGE),
MT6362_ADC_CHAN(5, IBUS, IIO_CURRENT),
MT6362_ADC_CHAN(6, IBAT, IIO_CURRENT),
MT6362_ADC_CHAN(7, RESV5, IIO_VOLTAGE),
MT6362_ADC_CHAN(8, TEMPJC, IIO_TEMP),
MT6362_ADC_CHAN(9, VREFTS, IIO_VOLTAGE),
MT6362_ADC_CHAN(10, TS, IIO_VOLTAGE),
MT6362_ADC_CHAN(11, PDVBUSDIV10, IIO_VOLTAGE),
MT6362_ADC_CHAN(12, PDCC1DIV4, IIO_VOLTAGE),
MT6362_ADC_CHAN(13, PDCC2DIV4, IIO_VOLTAGE),
MT6362_ADC_CHAN(14, PDSBU1DIV4, IIO_VOLTAGE),
MT6362_ADC_CHAN(15, PDSBU2DIV4, IIO_VOLTAGE),
MT6362_ADC_CHAN(17, ZCV, IIO_VOLTAGE),
IIO_CHAN_SOFT_TIMESTAMP(15),
};
static irqreturn_t mt6362_adc_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
int vals[16] = {0}; /* 14 ch s32 numbers + 1 s64 timestamp */
int dummy = 0, i, rv;
for_each_set_bit(i,
indio_dev->active_scan_mask, indio_dev->masklength) {
const struct iio_chan_spec *chan = indio_dev->channels + i;
rv = mt6362_adc_read_raw(indio_dev, chan, vals + i,
&dummy, IIO_CHAN_INFO_PROCESSED);
if (rv) {
dev_warn(&indio_dev->dev, "failed to get %d val\n", i);
goto out_trigger;
}
}
iio_push_to_buffers_with_timestamp(indio_dev,
vals, iio_get_time_ns(indio_dev));
out_trigger:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static irqreturn_t mt6362_adc_donei_irq(int irq, void *devid)
{
struct mt6362_adc_data *data = devid;
complete(&data->adc_comp);
return IRQ_HANDLED;
}
static int mt6362_adc_init(struct mt6362_adc_data *data)
{
/* adc en = 1, zcv = 0, all channels to be disabled */
const u8 adc_configs[] = { 0x80, 0x00, 0x00, 0x00, 0x00 };
return regmap_bulk_write(data->regmap, MT6362_REG_ADCCFG1,
adc_configs, sizeof(adc_configs));
}
static int mt6362_adc_init_offset_flags(struct mt6362_adc_data *data)
{
unsigned int devinfo, tminfo;
int rv;
rv = regmap_read(data->regmap, MT6362_REG_DEVINFO, &devinfo);
if (rv)
return rv;
rv = regmap_read(data->regmap, MT6362_REG_TMINFO, &tminfo);
if (rv)
return rv;
/* if rev > e2, adc convert time will be another one */
if ((devinfo & MT6362_CHIPREV_MASK) > MT6362_CHIPREV_E2)
data->conv_ltime_flag = true;
/* if rev > e2 or tmid3 == 1, need ibat offset, otherwise not */
if ((devinfo & MT6362_CHIPREV_MASK) > MT6362_CHIPREV_E2 ||
(tminfo & MT6362_TMID3_MASK))
data->ibat_offset_flag = true;
return 0;
}
static int mt6362_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct mt6362_adc_data *data;
int irq, rv;
dev_info(&pdev->dev, "%s\n", __func__);
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
mutex_init(&data->adc_lock);
init_completion(&data->adc_comp);
data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!data->regmap) {
dev_err(&pdev->dev, "failed to allocate regmap\n");
return -ENODEV;
}
rv = mt6362_adc_init_offset_flags(data);
if (rv) {
dev_notice(&pdev->dev, "failed to init adc offset flags\n");
return rv;
}
rv = mt6362_adc_init(data);
if (rv) {
dev_err(&pdev->dev, "failed to init adc [%d]\n", rv);
return rv;
}
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &mt6362_adc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mt6362_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(mt6362_adc_channels);
rv = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL,
mt6362_adc_trigger_handler, NULL);
if (rv) {
dev_err(&pdev->dev, "failed to allocate iio trigger buffer\n");
return rv;
}
irq = platform_get_irq_byname(pdev, "adc_donei");
if (irq <= 0) {
dev_err(&pdev->dev, "failed to get irq number\n");
return -EINVAL;
}
rv = devm_request_threaded_irq(&pdev->dev, irq, NULL,
mt6362_adc_donei_irq, 0, NULL, data);
if (rv) {
dev_err(&pdev->dev, "failed to request irq\n");
return rv;
}
rv = devm_iio_device_register(&pdev->dev, indio_dev);
if (rv) {
dev_err(&pdev->dev, "failed to register iio device\n");
return rv;
}
platform_set_drvdata(pdev, indio_dev);
dev_info(&pdev->dev, "%s: successful\n", __func__);
return 0;
}
static const struct of_device_id __maybe_unused mt6362_adc_ofid_tbls[] = {
{ .compatible = "mediatek,mt6362-adc", },
{ },
};
MODULE_DEVICE_TABLE(of, mt6362_adc_ofid_tbls);
static struct platform_driver mt6362_adc_driver = {
.driver = {
.name = "mt6362-adc",
.of_match_table = of_match_ptr(mt6362_adc_ofid_tbls),
},
.probe = mt6362_adc_probe,
};
#if 0
module_platform_driver(mt6362_adc_driver);
#else
static int __init mt6362_adc_driver_init(void)
{
return platform_driver_register(&mt6362_adc_driver);
}
static void __exit mt6362_adc_driver_exit(void)
{
platform_driver_unregister(&mt6362_adc_driver);
}
subsys_initcall(mt6362_adc_driver_init);
module_exit(mt6362_adc_driver_exit);
#endif
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("MT6362 SPMI ADC Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");