k1x:adc:p1: supprt adc driver for k1x

Change-Id: I200fe01c6bf351c13cc23876e4effca537ce028e
This commit is contained in:
Nell
2024-09-20 10:10:13 +08:00
committed by zhangmeng
parent fb6a7c6e9c
commit d6bbc3f057
19 changed files with 382 additions and 1 deletions

View File

@@ -623,6 +623,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -545,6 +545,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -507,6 +507,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -380,6 +380,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -625,6 +625,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
sgm4154x: sgm4154x@1a {

View File

@@ -646,6 +646,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -540,6 +540,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -532,6 +532,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -548,6 +548,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -633,6 +633,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -554,6 +554,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -361,6 +361,10 @@
ext_rtc: rtc {
compatible = "pmic,rtc,spm8821";
};
ext_adc: adc {
compatible = "pmic,adc,spm8821";
};
};
};

View File

@@ -1036,6 +1036,7 @@ CONFIG_EXTCON_USB_K1XCI=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_MXC4005=y
CONFIG_SPACEMIT_P1_ADC=y
CONFIG_STK3310=y
CONFIG_PWM=y
CONFIG_PWM_PXA=y

View File

@@ -1448,4 +1448,10 @@ config XILINX_AMS
The driver can also be built as a module. If so, the module will be called
xilinx-ams.
config SPACEMIT_P1_ADC
tristate "Spacemit P1 adc driver"
depends on MFD_SPACEMIT_PMIC
help
Say yes here to support for Spacemit P1 analog-to-digital converter.
endmenu

View File

@@ -129,3 +129,4 @@ xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
obj-$(CONFIG_SPACEMIT_P1_ADC) += k1x_adc.o

226
drivers/iio/adc/k1x_adc.c Normal file
View File

@@ -0,0 +1,226 @@
// 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/spacemit/spacemit_pmic.h>
struct spacemit_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 spacemit_adc_id_table[] = {
{ .compatible = "pmic,adc,spm8821", .data = (void *)&spm8821_adc_match_data },
{ }
};
MODULE_DEVICE_TABLE(of, spacemit_adc_id_table);
static int spacemit_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 spacemit_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 spacemit_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 spacemit_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 spacemit_spm8821_adc_raw(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
return spacemit_spm8821_adc_scale(chan, val, val2);
default:
return -EINVAL;
}
return -EINVAL;
}
static const struct iio_info spacemit_adc_iio_info = {
.read_raw = &spacemit_adc_read_raw,
};
static irqreturn_t adc_complete_irq(int irq, void *_adc)
{
struct spacemit_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 spacemit_adc_init(struct iio_dev *indio_dev)
{
struct spacemit_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 spacemit_adc_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct spacemit_adc_info *info;
const struct of_device_id *of_id;
struct spacemit_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(spacemit_adc_id_table, &pdev->dev);
if (!of_id) {
dev_err(&pdev->dev, "Unable to match OF ID\n");
return -ENODEV;
}
/* adc init */
spacemit_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 = &spacemit_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 spacemit_adc_driver = {
.probe = spacemit_adc_probe,
.driver = {
.name = "spacemit-pmic-adc",
.of_match_table = of_match_ptr(spacemit_adc_id_table),
},
};
module_platform_driver(spacemit_adc_driver);
MODULE_DESCRIPTION("SPACEMIT adc driver");
MODULE_LICENSE("GPL v2");

View File

@@ -16,6 +16,7 @@ SPM8821_IRQS_DESC;
SPM8821_IRQ_CHIP_DESC;
SPM8821_POWER_KEY_RESOURCES_DESC;
SPM8821_RTC_RESOURCES_DESC;
SPM8821_ADC_RESOURCES_DESC;
SPM8821_MFD_CELL;
SPM8821_MFD_MATCH_DATA;

View File

@@ -213,6 +213,12 @@ struct pinctrl_match_data {
const char *name;
};
struct adc_match_data {
int nr_desc;
const char *name;
struct iio_chan_spec *iio_desc;
};
/* common regulator defination */
#define SPM8XX_DESC_COMMON(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr, _ops) \
{ \

View File

@@ -24,6 +24,8 @@ enum SPM8821_reg {
#define SPACEMIT_SPM8821_MAX_REG 0xB0
#define SPM8821_VERSION_ID_REG 0xa1
#define SPM8821_BUCK_VSEL_MASK 0xff
#define SMP8821_BUCK_EN_MASK 0x1
@@ -85,6 +87,37 @@ enum SPM8821_reg {
#define SPM8821_SLEEP_REG_OFFSET 0x1
#define SPM8821_ADC_AUTO_REG 0x22
#define SPM8821_ADC_AUTO_BIT_MSK 0x7f
#define SPM8821_ADC_CTRL_REG 0x1e
#define SPM8821_ADC_CTRL_BIT_MSK 0x3
#define SPM8821_ADC_CTRL_EN_BIT_OFFSET 0x0
#define SPM8821_ADC_CTRL_GO_BIT_OFFSET 0x1
#define SPM8821_ADC_CFG1_REG 0x20
#define SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_OFFSET 0x6
#define SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_MSK 0x40
#define SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_OFFSET 0x3
#define SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_MSK 0x38
#define SPM8821_ADC_CFG2_REG 0x21
#define SPM8821_ADC_CFG2_REF_SEL_BIT_OFFSET 0x0
#define SPM8821_ADC_CFG2_REF_SEL_BIT_MSK 0x3
#define SPM8821_ADC_CFG2_3V3_REF 0x2
#define SPM8821_ADC_CFG2_7_DEB_NUM 0x7
#define SPM8821_ADC_CFG2_DEB_NUM_BIT_MSK 0x70
#define SPM8821_ADC_CFG2_DEB_NUM_BIT_OFFSET 0x4
#define SPM8821_ADC_EXTERNAL_CHANNEL_OFFSET 2
#define SPM8821_ADCIN0_RES_H_REG 0x2a
#define SPM8821_ADCIN0_RES_L_REG 0x2b
#define SPM8821_ADCIN0_REG_L_BIT_MSK 0xf0
#define SPM8821_REGMAP_CONFIG \
static const struct regmap_config spm8821_regmap_config = { \
.reg_bits = 8, \
@@ -197,6 +230,46 @@ static const struct regulator_desc spm8821_reg[] = { \
SPM8821_DESC_SWITCH(SPM8821_ID_SWITCH1, "SWITCH_REG1", "vcc_sys", SPM8821_SWITCH_CTRL_REG, SPM8821_SWTICH_EN_MASK), \
};
#define SPM8821_ADC_IIO_DESC \
static struct iio_chan_spec spm8821_iio_desc[] = { \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 0, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 2, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 3, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 4, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
{ \
.indexed = 1, \
.type = IIO_VOLTAGE, \
.channel = 5, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
}, \
};
/* gpio set */
#define SPM8821_PINMUX_DESC \
const char* spm8821_pinmux_functions[] = { \
@@ -664,6 +737,12 @@ static const struct resource spm8821_rtc_resources[] = { \
DEFINE_RES_IRQ(SPM8821_E_ALARM), \
};
/* adc desc */
#define SPM8821_ADC_RESOURCES_DESC \
static const struct resource spm8821_adc_resources[] = { \
DEFINE_RES_IRQ(SPM8821_E_ADC_EOC), \
};
#define SPM8821_RTC_REG_DESC \
static const struct rtc_regdesc spm8821_regdesc = { \
.cnt_s = { \
@@ -754,6 +833,12 @@ static const struct rtc_regdesc spm8821_regdesc = { \
.num_resources = ARRAY_SIZE(spm8821_rtc_resources), \
.resources = &spm8821_rtc_resources[0], \
}, \
{ \
.name = "spacemit-adc@spm8821", \
.of_compatible = "pmic,adc,spm8821", \
.num_resources = ARRAY_SIZE(spm8821_adc_resources), \
.resources = &spm8821_adc_resources[0], \
}, \
};
#define SPM8821_MFD_MATCH_DATA \
@@ -778,7 +863,7 @@ static struct mfd_match_data spm8821_mfd_match_data = { \
};
#define SPM8821_PINCTRL_MATCH_DATA \
static struct pinctrl_match_data spm8821_pinctrl_match_data = { \
static struct pinctrl_match_data spm8821_pinctrl_match_data = { \
.nr_pin_mux = ARRAY_SIZE(spm8821_pinmux_functions), \
.pinmux_funcs = spm8821_pinmux_functions, \
.nr_pin_fuc_desc = ARRAY_SIZE(spm8821_pinfunc_desc), \
@@ -796,4 +881,11 @@ static struct regulator_match_data spm8821_regulator_match_data = { \
.sleep_reg_offset = SPM8821_SLEEP_REG_OFFSET, \
};
#define SPM8821_ADC_MATCH_DATA \
static struct adc_match_data spm8821_adc_match_data = { \
.iio_desc = spm8821_iio_desc, \
.nr_desc = ARRAY_SIZE(spm8821_iio_desc), \
.name = "spm8821", \
};
#endif /* __SPM8821_H__ */