1
0
This repository has been archived on 2025-07-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
orange_kernel/drivers/iio/adc/x1_adc.c
2025-03-18 10:29:27 +08:00

227 lines
5.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/of.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/of_device.h>
#include <linux/iio/driver.h>
#include <linux/mfd/ky/ky_pmic.h>
struct ky_adc_info {
int irq;
struct regmap *regmap;
struct mutex lock;
struct completion completion;
};
static struct adc_match_data *match_data;
SPM8821_ADC_IIO_DESC;
SPM8821_ADC_MATCH_DATA;
static const struct of_device_id ky_adc_id_table[] = {
{ .compatible = "pmic,adc,spm8821", .data = (void *)&spm8821_adc_match_data },
{ }
};
MODULE_DEVICE_TABLE(of, ky_adc_id_table);
static int ky_spm8821_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val)
{
unsigned int value;
unsigned int adc_val_h, adc_val_l;
struct ky_adc_info *info;
info = iio_priv(indio_dev);
mutex_lock(&info->lock);
/* reset the ADC auto register */
regmap_update_bits(info->regmap, SPM8821_ADC_AUTO_REG,
SPM8821_ADC_AUTO_BIT_MSK, 0);
/* enable the ADC : ADC_CTRL[0] */
regmap_update_bits(info->regmap, SPM8821_ADC_CTRL_REG,
SPM8821_ADC_CTRL_BIT_MSK, (1 << SPM8821_ADC_CTRL_EN_BIT_OFFSET));
/* choose the channel of adc : ADC_CFG[1] */
regmap_update_bits(info->regmap, SPM8821_ADC_CFG1_REG,
SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_MSK,
(chan->channel + SPM8821_ADC_EXTERNAL_CHANNEL_OFFSET) <<
SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_OFFSET);
/* ADC go */
regmap_update_bits(info->regmap, SPM8821_ADC_CTRL_REG,
SPM8821_ADC_CTRL_BIT_MSK, (1 << SPM8821_ADC_CTRL_GO_BIT_OFFSET) |
(1 << SPM8821_ADC_CTRL_EN_BIT_OFFSET));
/* then wait the completion */
wait_for_completion(&info->completion);
regmap_read(info->regmap, SPM8821_ADCIN0_RES_H_REG + chan->channel * 2, &adc_val_h);
regmap_read(info->regmap, SPM8821_ADCIN0_RES_L_REG + chan->channel * 2, &adc_val_l);
regmap_read(info->regmap, SPM8821_VERSION_ID_REG, &value);
*val = (adc_val_h << (ffs(SPM8821_ADCIN0_REG_L_BIT_MSK) - 1)) | (
(adc_val_l & SPM8821_ADCIN0_REG_L_BIT_MSK) >>
(ffs(SPM8821_ADCIN0_REG_L_BIT_MSK) - 1));
if (value == 0) {
/**
* if the version of P1 is A, the data read from the register is the inverse of the real data
* and the conversion accuracy of P1 is 12 bits
*/
*val = 4095 - *val;
}
pr_debug("%s:%d, read channel:%d, val:%u\n", __func__, __LINE__, chan->channel,
*val);
mutex_unlock(&info->lock);
return IIO_VAL_INT;
}
static int ky_spm8821_adc_scale(struct iio_chan_spec const *chan, int *val, int *val2)
{
switch (chan->type) {
case IIO_VOLTAGE:
*val = 0;
/* 3000 % 4095 ~ 0.7326mv */
*val2 = 732600;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
return -EINVAL;
}
static int ky_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_RAW:
return ky_spm8821_adc_raw(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
return ky_spm8821_adc_scale(chan, val, val2);
default:
return -EINVAL;
}
return -EINVAL;
}
static const struct iio_info ky_adc_iio_info = {
.read_raw = &ky_adc_read_raw,
};
static irqreturn_t adc_complete_irq(int irq, void *_adc)
{
struct ky_adc_info *info;
struct iio_dev *indio_dev = (struct iio_dev *)_adc;
info = iio_priv(indio_dev);
complete(&info->completion);
return IRQ_HANDLED;
}
static void ky_adc_init(struct iio_dev *indio_dev)
{
struct ky_adc_info *info = iio_priv(indio_dev);
/* enable chop */
regmap_update_bits(info->regmap, SPM8821_ADC_CFG1_REG,
SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_MSK, 1 << SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_OFFSET);
/* set the vref: 3v3 */
regmap_update_bits(info->regmap, SPM8821_ADC_CFG2_REG,
SPM8821_ADC_CFG2_REF_SEL_BIT_MSK, SPM8821_ADC_CFG2_3V3_REF <<
SPM8821_ADC_CFG2_REF_SEL_BIT_OFFSET);
/* set adc deb num: 7 */
regmap_update_bits(info->regmap, SPM8821_ADC_CFG2_REG,
SPM8821_ADC_CFG2_DEB_NUM_BIT_MSK, SPM8821_ADC_CFG2_7_DEB_NUM <<
SPM8821_ADC_CFG2_DEB_NUM_BIT_OFFSET);
}
static int ky_adc_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ky_adc_info *info;
const struct of_device_id *of_id;
struct ky_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
dev_err(&pdev->dev, "get irq failed\n");
return info->irq;
}
ret = devm_request_any_context_irq(&pdev->dev, info->irq,
adc_complete_irq, IRQF_TRIGGER_NONE | IRQF_ONESHOT,
"p1-adc", indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "Can't register adc irq: %d\n", ret);
return ret;
}
info->regmap = pmic->regmap;
mutex_init(&info->lock);
init_completion(&info->completion);
of_id = of_match_device(ky_adc_id_table, &pdev->dev);
if (!of_id) {
dev_err(&pdev->dev, "Unable to match OF ID\n");
return -ENODEV;
}
/* adc init */
ky_adc_init(indio_dev);
match_data = (struct adc_match_data *)of_id->data;
indio_dev->name = pdev->name;
indio_dev->channels = match_data->iio_desc;
indio_dev->num_channels = match_data->nr_desc;
indio_dev->info = &ky_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_iio_map_array_register(&pdev->dev, indio_dev, NULL);
if (ret < 0)
return ret;
return devm_iio_device_register(&pdev->dev, indio_dev);
}
static struct platform_driver ky_adc_driver = {
.probe = ky_adc_probe,
.driver = {
.name = "ky-pmic-adc",
.of_match_table = of_match_ptr(ky_adc_id_table),
},
};
module_platform_driver(ky_adc_driver);
MODULE_DESCRIPTION("KY adc driver");
MODULE_LICENSE("GPL v2");