aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/iio/adc/aspeed_adc.c71
1 files changed, 71 insertions, 0 deletions
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 72c7e0ea0769..9e2c85c3cbe9 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -25,6 +25,8 @@
#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>
@@ -80,6 +82,11 @@
*/
#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
@@ -90,6 +97,7 @@ struct aspeed_adc_model_data {
bool bat_sense_sup;
u8 scaler_bit_width;
unsigned int num_channels;
+ const struct aspeed_adc_trim_locate *trim_locate;
};
struct adc_gain {
@@ -165,6 +173,44 @@ static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = {
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);
@@ -513,6 +559,13 @@ static int aspeed_adc_probe(struct platform_device *pdev)
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) {
@@ -589,6 +642,21 @@ static int aspeed_adc_probe(struct platform_device *pdev)
return ret;
}
+static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
+ .offset = 0x154,
+ .field = GENMASK(31, 28),
+};
+
+static const struct aspeed_adc_trim_locate ast2600_adc0_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(3, 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_fixed_mv = 2500,
@@ -608,6 +676,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = {
.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 = {
@@ -618,6 +687,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = {
.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 = {
@@ -628,6 +698,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = {
.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[] = {