summaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc/aspeed_adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/aspeed_adc.c')
-rw-r--r--drivers/iio/adc/aspeed_adc.c598
1 files changed, 496 insertions, 102 deletions
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 34ec0c28b2df..e939b84cbb56 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -1,8 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Aspeed AST2400/2500 ADC
+ * Aspeed AST2400/2500/2600 ADC
*
* Copyright (C) 2017 Google, Inc.
+ * Copyright (C) 2021 Aspeed Technology Inc.
+ *
+ * ADC clock formula:
+ * Ast2400/Ast2500:
+ * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1)
+ * Ast2600:
+ * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1)
*/
#include <linux/clk.h>
@@ -13,9 +20,13 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
@@ -28,34 +39,87 @@
#define ASPEED_REG_INTERRUPT_CONTROL 0x04
#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
#define ASPEED_REG_CLOCK_CONTROL 0x0C
-#define ASPEED_REG_MAX 0xC0
-
-#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
-#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
-#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
-
-#define ASPEED_ENGINE_ENABLE BIT(0)
-
-#define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
+#define ASPEED_REG_COMPENSATION_TRIM 0xC4
+/*
+ * The register offset between 0xC8~0xCC can be read and won't affect the
+ * hardware logic in each version of ADC.
+ */
+#define ASPEED_REG_MAX 0xD0
+
+#define ASPEED_ADC_ENGINE_ENABLE BIT(0)
+#define ASPEED_ADC_OP_MODE GENMASK(3, 1)
+#define ASPEED_ADC_OP_MODE_PWR_DOWN 0
+#define ASPEED_ADC_OP_MODE_STANDBY 1
+#define ASPEED_ADC_OP_MODE_NORMAL 7
+#define ASPEED_ADC_CTRL_COMPENSATION BIT(4)
+#define ASPEED_ADC_AUTO_COMPENSATION BIT(5)
+/*
+ * Bit 6 determines not only the reference voltage range but also the dividing
+ * circuit for battery sensing.
+ */
+#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6)
+#define ASPEED_ADC_REF_VOLTAGE_2500mV 0
+#define ASPEED_ADC_REF_VOLTAGE_1200mV 1
+#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2
+#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3
+#define ASPEED_ADC_BAT_SENSING_DIV BIT(6)
+#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0
+#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1
+#define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
+#define ASPEED_ADC_CH7_MODE BIT(12)
+#define ASPEED_ADC_CH7_NORMAL 0
+#define ASPEED_ADC_CH7_BAT 1
+#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
+#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
+#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
#define ASPEED_ADC_INIT_POLLING_TIME 500
#define ASPEED_ADC_INIT_TIMEOUT 500000
+/*
+ * When the sampling rate is too high, the ADC may not have enough charging
+ * time, resulting in a low voltage value. Thus, the default uses a slow
+ * sampling rate for most use cases.
+ */
+#define ASPEED_ADC_DEF_SAMPLING_RATE 65000
+
+struct aspeed_adc_trim_locate {
+ const unsigned int offset;
+ const unsigned int field;
+};
struct aspeed_adc_model_data {
const char *model_name;
unsigned int min_sampling_rate; // Hz
unsigned int max_sampling_rate; // Hz
- unsigned int vref_voltage; // mV
+ unsigned int vref_fixed_mv;
bool wait_init_sequence;
+ bool need_prescaler;
+ bool bat_sense_sup;
+ u8 scaler_bit_width;
+ unsigned int num_channels;
+ const struct aspeed_adc_trim_locate *trim_locate;
+};
+
+struct adc_gain {
+ u8 mult;
+ u8 div;
};
struct aspeed_adc_data {
struct device *dev;
+ const struct aspeed_adc_model_data *model_data;
+ struct regulator *regulator;
void __iomem *base;
spinlock_t clk_lock;
+ struct clk_hw *fixed_div_clk;
struct clk_hw *clk_prescaler;
struct clk_hw *clk_scaler;
struct reset_control *rst;
+ int vref_mv;
+ u32 sample_period_ns;
+ int cv;
+ bool battery_sensing;
+ struct adc_gain battery_mode_gain;
};
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
@@ -65,7 +129,8 @@ struct aspeed_adc_data {
.address = (_data_reg_addr), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
}
static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
@@ -87,21 +152,170 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
ASPEED_CHAN(15, 0x2E),
};
+#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .address = (_data_reg_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = {
+ ASPEED_CHAN(0, 0x10),
+ ASPEED_CHAN(1, 0x12),
+ ASPEED_CHAN(2, 0x14),
+ ASPEED_CHAN(3, 0x16),
+ ASPEED_CHAN(4, 0x18),
+ ASPEED_CHAN(5, 0x1A),
+ ASPEED_CHAN(6, 0x1C),
+ ASPEED_BAT_CHAN(7, 0x1E),
+};
+
+static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev)
+{
+ struct device_node *syscon;
+ struct regmap *scu;
+ u32 scu_otp, trimming_val;
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ syscon = of_find_node_by_name(NULL, "syscon");
+ if (syscon == NULL) {
+ dev_warn(data->dev, "Couldn't find syscon node\n");
+ return -EOPNOTSUPP;
+ }
+ scu = syscon_node_to_regmap(syscon);
+ if (IS_ERR(scu)) {
+ dev_warn(data->dev, "Failed to get syscon regmap\n");
+ return -EOPNOTSUPP;
+ }
+ if (data->model_data->trim_locate) {
+ if (regmap_read(scu, data->model_data->trim_locate->offset,
+ &scu_otp)) {
+ dev_warn(data->dev,
+ "Failed to get adc trimming data\n");
+ trimming_val = 0x8;
+ } else {
+ trimming_val =
+ ((scu_otp) &
+ (data->model_data->trim_locate->field)) >>
+ __ffs(data->model_data->trim_locate->field);
+ }
+ dev_dbg(data->dev,
+ "trimming val = %d, offset = %08x, fields = %08x\n",
+ trimming_val, data->model_data->trim_locate->offset,
+ data->model_data->trim_locate->field);
+ writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM);
+ }
+ return 0;
+}
+
+static int aspeed_adc_compensation(struct iio_dev *indio_dev)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ u32 index, adc_raw = 0;
+ u32 adc_engine_control_reg_val;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE;
+ adc_engine_control_reg_val |=
+ (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE);
+ /*
+ * Enable compensating sensing:
+ * After that, the input voltage of ADC will force to half of the reference
+ * voltage. So the expected reading raw data will become half of the max
+ * value. We can get compensating value = 0x200 - ADC read raw value.
+ * It is recommended to average at least 10 samples to get a final CV.
+ */
+ writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION |
+ ASPEED_ADC_CTRL_CHANNEL_ENABLE(0),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable compensating sensing mode need to wait some time for ADC stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+
+ for (index = 0; index < 16; index++) {
+ /*
+ * Waiting for the sampling period ensures that the value acquired
+ * is fresh each time.
+ */
+ ndelay(data->sample_period_ns);
+ adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address);
+ }
+ adc_raw >>= 4;
+ data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ dev_dbg(data->dev, "Compensating value = %d\n", data->cv);
+
+ return 0;
+}
+
+static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ if (rate < data->model_data->min_sampling_rate ||
+ rate > data->model_data->max_sampling_rate)
+ return -EINVAL;
+ /* Each sampling needs 12 clocks to convert.*/
+ clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE);
+ rate = clk_get_rate(data->clk_scaler->clk);
+ data->sample_period_ns = DIV_ROUND_UP_ULL(
+ (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate);
+ dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate,
+ data->sample_period_ns);
+
+ return 0;
+}
+
static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct aspeed_adc_data *data = iio_priv(indio_dev);
- const struct aspeed_adc_model_data *model_data =
- of_device_get_match_data(data->dev);
+ u32 adc_engine_control_reg_val;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- *val = readw(data->base + chan->address);
+ if (data->battery_sensing && chan->channel == 7) {
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_CH7_MODE,
+ ASPEED_ADC_CH7_BAT) |
+ ASPEED_ADC_BAT_SENSING_ENABLE,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable battery sensing mode need to wait some time for adc stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+ *val = readw(data->base + chan->address);
+ *val = (*val * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ /* Restore control register value */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ } else
+ *val = readw(data->base + chan->address);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (data->battery_sensing && chan->channel == 7)
+ *val = (data->cv * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ else
+ *val = data->cv;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = model_data->vref_voltage;
+ *val = data->vref_mv;
*val2 = ASPEED_RESOLUTION_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -119,19 +333,9 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- struct aspeed_adc_data *data = iio_priv(indio_dev);
- const struct aspeed_adc_model_data *model_data =
- of_device_get_match_data(data->dev);
-
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- if (val < model_data->min_sampling_rate ||
- val > model_data->max_sampling_rate)
- return -EINVAL;
-
- clk_set_rate(data->clk_scaler->clk,
- val * ASPEED_CLOCKS_PER_SAMPLE);
- return 0;
+ return aspeed_adc_set_sampling_rate(indio_dev, val);
case IIO_CHAN_INFO_SCALE:
case IIO_CHAN_INFO_RAW:
@@ -168,14 +372,119 @@ static const struct iio_info aspeed_adc_iio_info = {
.debugfs_reg_access = aspeed_adc_reg_access,
};
+static void aspeed_adc_unregister_fixed_divider(void *data)
+{
+ struct clk_hw *clk = data;
+
+ clk_hw_unregister_fixed_factor(clk);
+}
+
+static void aspeed_adc_reset_assert(void *data)
+{
+ struct reset_control *rst = data;
+
+ reset_control_assert(rst);
+}
+
+static void aspeed_adc_clk_disable_unprepare(void *data)
+{
+ struct clk *clk = data;
+
+ clk_disable_unprepare(clk);
+}
+
+static void aspeed_adc_power_down(void *data)
+{
+ struct aspeed_adc_data *priv_data = data;
+
+ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN),
+ priv_data->base + ASPEED_REG_ENGINE_CONTROL);
+}
+
+static void aspeed_adc_reg_disable(void *data)
+{
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+}
+
+static int aspeed_adc_vref_config(struct iio_dev *indio_dev)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ int ret;
+ u32 adc_engine_control_reg_val;
+
+ if (data->model_data->vref_fixed_mv) {
+ data->vref_mv = data->model_data->vref_fixed_mv;
+ return 0;
+ }
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ data->regulator = devm_regulator_get_optional(data->dev, "vref");
+ if (!IS_ERR(data->regulator)) {
+ ret = regulator_enable(data->regulator);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(
+ data->dev, aspeed_adc_reg_disable, data->regulator);
+ if (ret)
+ return ret;
+ data->vref_mv = regulator_get_voltage(data->regulator);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_HIGH),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_LOW),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Regulator voltage %d not support",
+ data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ } else {
+ if (PTR_ERR(data->regulator) != -ENODEV)
+ return PTR_ERR(data->regulator);
+ data->vref_mv = 2500000;
+ of_property_read_u32(data->dev->of_node,
+ "aspeed,int-vref-microvolt",
+ &data->vref_mv);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if (data->vref_mv == 2500)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_2500mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if (data->vref_mv == 1200)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_1200mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Voltage %d not support", data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
static int aspeed_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct aspeed_adc_data *data;
- const struct aspeed_adc_model_data *model_data;
- const char *clk_parent_name;
int ret;
u32 adc_engine_control_reg_val;
+ unsigned long scaler_flags = 0;
+ char clk_name[32], clk_parent_name[32];
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev)
@@ -183,6 +492,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
+ data->model_data = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
@@ -191,45 +501,117 @@ static int aspeed_adc_probe(struct platform_device *pdev)
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
- clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
-
- data->clk_prescaler = clk_hw_register_divider(
- &pdev->dev, "prescaler", clk_parent_name, 0,
- data->base + ASPEED_REG_CLOCK_CONTROL,
- 17, 15, 0, &data->clk_lock);
- if (IS_ERR(data->clk_prescaler))
- return PTR_ERR(data->clk_prescaler);
-
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
+ of_clk_get_parent_name(pdev->dev.of_node, 0));
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
+ data->model_data->model_name);
+ data->fixed_div_clk = clk_hw_register_fixed_factor(
+ &pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
+ if (IS_ERR(data->fixed_div_clk))
+ return PTR_ERR(data->fixed_div_clk);
+
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_unregister_fixed_divider,
+ data->fixed_div_clk);
+ if (ret)
+ return ret;
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name);
+
+ if (data->model_data->need_prescaler) {
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
+ data->model_data->model_name);
+ data->clk_prescaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, 0,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
+ &data->clk_lock);
+ if (IS_ERR(data->clk_prescaler))
+ return PTR_ERR(data->clk_prescaler);
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name),
+ clk_name);
+ scaler_flags = CLK_SET_RATE_PARENT;
+ }
/*
* Register ADC clock scaler downstream from the prescaler. Allow rate
* setting to adjust the prescaler as well.
*/
- data->clk_scaler = clk_hw_register_divider(
- &pdev->dev, "scaler", "prescaler",
- CLK_SET_RATE_PARENT,
- data->base + ASPEED_REG_CLOCK_CONTROL,
- 0, 10, 0, &data->clk_lock);
- if (IS_ERR(data->clk_scaler)) {
- ret = PTR_ERR(data->clk_scaler);
- goto scaler_error;
- }
-
- data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
+ data->model_data->model_name);
+ data->clk_scaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, scaler_flags,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 0,
+ data->model_data->scaler_bit_width, 0, &data->clk_lock);
+ if (IS_ERR(data->clk_scaler))
+ return PTR_ERR(data->clk_scaler);
+
+ data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(data->rst)) {
dev_err(&pdev->dev,
"invalid or missing reset controller device tree entry");
- ret = PTR_ERR(data->rst);
- goto reset_error;
+ return PTR_ERR(data->rst);
}
reset_control_deassert(data->rst);
- model_data = of_device_get_match_data(&pdev->dev);
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
+ data->rst);
+ if (ret)
+ return ret;
- if (model_data->wait_init_sequence) {
- /* Enable engine in normal mode. */
- writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE,
- data->base + ASPEED_REG_ENGINE_CONTROL);
+ ret = aspeed_adc_vref_config(indio_dev);
+ if (ret)
+ return ret;
+ if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid",
+ NULL)) {
+ ret = aspeed_adc_set_trim_data(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
+ NULL)) {
+ if (data->model_data->bat_sense_sup) {
+ data->battery_sensing = 1;
+ if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
+ ASPEED_ADC_BAT_SENSING_DIV) {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 1;
+ } else {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 2;
+ }
+ } else
+ dev_warn(&pdev->dev,
+ "Failed to enable battery-sensing mode\n");
+ }
+
+ ret = clk_prepare_enable(data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_clk_disable_unprepare,
+ data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = aspeed_adc_set_sampling_rate(indio_dev,
+ ASPEED_ADC_DEF_SAMPLING_RATE);
+ if (ret)
+ return ret;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |=
+ FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE;
+ /* Enable engine in normal mode. */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
+ data);
+ if (ret)
+ return ret;
+
+ if (data->model_data->wait_init_sequence) {
/* Wait for initial sequence complete. */
ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
adc_engine_control_reg_val,
@@ -238,87 +620,99 @@ static int aspeed_adc_probe(struct platform_device *pdev)
ASPEED_ADC_INIT_POLLING_TIME,
ASPEED_ADC_INIT_TIMEOUT);
if (ret)
- goto poll_timeout_error;
+ return ret;
}
+ aspeed_adc_compensation(indio_dev);
/* Start all channels in normal mode. */
- ret = clk_prepare_enable(data->clk_scaler->clk);
- if (ret)
- goto clk_enable_error;
-
- adc_engine_control_reg_val = GENMASK(31, 16) |
- ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
writel(adc_engine_control_reg_val,
- data->base + ASPEED_REG_ENGINE_CONTROL);
+ data->base + ASPEED_REG_ENGINE_CONTROL);
- model_data = of_device_get_match_data(&pdev->dev);
- indio_dev->name = model_data->model_name;
+ indio_dev->name = data->model_data->model_name;
indio_dev->info = &aspeed_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = aspeed_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
-
- ret = iio_device_register(indio_dev);
- if (ret)
- goto iio_register_error;
-
- return 0;
+ indio_dev->channels = data->battery_sensing ?
+ aspeed_adc_iio_bat_channels :
+ aspeed_adc_iio_channels;
+ indio_dev->num_channels = data->model_data->num_channels;
-iio_register_error:
- writel(ASPEED_OPERATION_MODE_POWER_DOWN,
- data->base + ASPEED_REG_ENGINE_CONTROL);
- clk_disable_unprepare(data->clk_scaler->clk);
-clk_enable_error:
-poll_timeout_error:
- reset_control_assert(data->rst);
-reset_error:
- clk_hw_unregister_divider(data->clk_scaler);
-scaler_error:
- clk_hw_unregister_divider(data->clk_prescaler);
+ ret = devm_iio_device_register(data->dev, indio_dev);
return ret;
}
-static int aspeed_adc_remove(struct platform_device *pdev)
-{
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct aspeed_adc_data *data = iio_priv(indio_dev);
+static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
+ .offset = 0x154,
+ .field = GENMASK(31, 28),
+};
- iio_device_unregister(indio_dev);
- writel(ASPEED_OPERATION_MODE_POWER_DOWN,
- data->base + ASPEED_REG_ENGINE_CONTROL);
- clk_disable_unprepare(data->clk_scaler->clk);
- reset_control_assert(data->rst);
- clk_hw_unregister_divider(data->clk_scaler);
- clk_hw_unregister_divider(data->clk_prescaler);
+static const struct aspeed_adc_trim_locate ast2600_adc0_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(3, 0),
+};
- return 0;
-}
+static const struct aspeed_adc_trim_locate ast2600_adc1_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(7, 4),
+};
static const struct aspeed_adc_model_data ast2400_model_data = {
.model_name = "ast2400-adc",
- .vref_voltage = 2500, // mV
+ .vref_fixed_mv = 2500,
.min_sampling_rate = 10000,
.max_sampling_rate = 500000,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
};
static const struct aspeed_adc_model_data ast2500_model_data = {
.model_name = "ast2500-adc",
- .vref_voltage = 1800, // mV
+ .vref_fixed_mv = 1800,
.min_sampling_rate = 1,
.max_sampling_rate = 1000000,
.wait_init_sequence = true,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
+ .trim_locate = &ast2500_adc_trim,
+};
+
+static const struct aspeed_adc_model_data ast2600_adc0_model_data = {
+ .model_name = "ast2600-adc0",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc0_trim,
+};
+
+static const struct aspeed_adc_model_data ast2600_adc1_model_data = {
+ .model_name = "ast2600-adc1",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc1_trim,
};
static const struct of_device_id aspeed_adc_matches[] = {
{ .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
+ { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data },
+ { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data },
{},
};
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
static struct platform_driver aspeed_adc_driver = {
.probe = aspeed_adc_probe,
- .remove = aspeed_adc_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_adc_matches,
@@ -328,5 +722,5 @@ static struct platform_driver aspeed_adc_driver = {
module_platform_driver(aspeed_adc_driver);
MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
-MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
+MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver");
MODULE_LICENSE("GPL");