This series adds support for the AXP192 PMIC to the AXP20x MFD driver
framework, including support for regulators, ADCs, and AC/USB/battery
power supplies.
v6 is a resend of v5 from July -- the patches haven't changed at all
but I've rebased them on the latest git master branch.
Aidan MacDonald (13):
dt-bindings: mfd: add bindings for AXP192 MFD device
dt-bindings: iio: adc: axp209: Add AXP192 compatible
dt-bindings: power: supply: axp20x: Add AXP192 compatible
dt-bindings: power: axp20x-battery: Add AXP192 compatible
mfd: axp20x: Add support for AXP192
regulator: axp20x: Add support for AXP192
iio: adc: axp20x_adc: Minor code cleanups
iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field
iio: adc: axp20x_adc: Add support for AXP192
power: supply: axp20x_usb_power: Add support for AXP192
power: axp20x_battery: Add constant charge current table
power: axp20x_battery: Support battery status without fuel gauge
power: axp20x_battery: Add support for AXP192
.../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 +
.../bindings/mfd/x-powers,axp152.yaml | 1 +
.../x-powers,axp20x-battery-power-supply.yaml | 1 +
.../x-powers,axp20x-usb-power-supply.yaml | 1 +
drivers/iio/adc/axp20x_adc.c | 356 ++++++++++++++++--
drivers/mfd/axp20x-i2c.c | 2 +
drivers/mfd/axp20x.c | 141 +++++++
drivers/power/supply/axp20x_battery.c | 142 ++++++-
drivers/power/supply/axp20x_usb_power.c | 84 ++++-
drivers/regulator/axp20x-regulator.c | 100 ++++-
include/linux/mfd/axp20x.h | 84 +++++
11 files changed, 856 insertions(+), 74 deletions(-)
--
2.38.0
The AXP192 is another X-Powers PMIC similar to the existing ones.
Acked-by: Rob Herring <[email protected]>
Acked-by: Chen-Yu Tsai <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml b/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml
index b7a8747d5fa0..db9845add060 100644
--- a/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml
+++ b/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml
@@ -84,6 +84,7 @@ properties:
oneOf:
- enum:
- x-powers,axp152
+ - x-powers,axp192
- x-powers,axp202
- x-powers,axp209
- x-powers,axp221
--
2.38.0
The AXP192 is identical to the AXP20x, except for two additional
GPIO ADC channels.
Acked-by: Rob Herring <[email protected]>
Reviewed-by: Chen-Yu Tsai <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
.../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
index d6d3d8590171..1a68e650ac7d 100644
--- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
@@ -14,6 +14,23 @@ description: |
Device is a child of an axp209 multifunction device
ADC channels and their indexes per variant:
+ AXP192
+ ------
+ 0 | acin_v
+ 1 | acin_i
+ 2 | vbus_v
+ 3 | vbus_i
+ 4 | pmic_temp
+ 5 | gpio0_v
+ 6 | gpio1_v
+ 7 | gpio2_v
+ 8 | gpio3_v
+ 9 | ipsout_v
+ 10 | batt_v
+ 11 | batt_chrg_i
+ 12 | batt_dischrg_i
+ 13 | ts_v
+
AXP209
------
0 | acin_v
@@ -50,6 +67,7 @@ description: |
properties:
compatible:
oneOf:
+ - const: x-powers,axp192-adc
- const: x-powers,axp209-adc
- const: x-powers,axp221-adc
- const: x-powers,axp813-adc
--
2.38.0
The AXP192's USB power supply is similar to the AXP202 but it has
different USB current limits and a different offset for the VBUS
status register.
Acked-by: Rob Herring <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
.../bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
index 3ce648dd91bd..34b7959d6772 100644
--- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
+++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
@@ -22,6 +22,7 @@ properties:
compatible:
oneOf:
- enum:
+ - x-powers,axp192-usb-power-supply
- x-powers,axp202-usb-power-supply
- x-powers,axp221-usb-power-supply
- x-powers,axp223-usb-power-supply
--
2.38.0
The AXP192's USB power supply is similar to the AXP202 but it has
different USB current limits and a different offset for the VBUS
status register.
Acked-by: Sebastian Reichel <[email protected]>
Reviewed-by: Chen-Yu Tsai <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/power/supply/axp20x_usb_power.c | 84 +++++++++++++++++++++----
1 file changed, 73 insertions(+), 11 deletions(-)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index a1e6d1d44808..f83e2ed6d507 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -48,6 +48,9 @@
#define AXP813_VBUS_CLIMIT_2000mA 2
#define AXP813_VBUS_CLIMIT_2500mA 3
+#define AXP192_VBUS_CLIMIT_EN BIT(1)
+#define AXP192_VBUS_CLIMIT_100mA BIT(0)
+
#define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
@@ -121,6 +124,25 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
}
+static int axp192_get_current_max(struct axp20x_usb_power *power, int *val)
+{
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
+ if (ret)
+ return ret;
+
+ if (!(v & AXP192_VBUS_CLIMIT_EN))
+ *val = -1;
+ else if (v & AXP192_VBUS_CLIMIT_100mA)
+ *val = 100000;
+ else
+ *val = 500000;
+
+ return 0;
+}
+
static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val)
{
unsigned int v;
@@ -179,7 +201,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
- unsigned int input, v;
+ unsigned int input, v, reg;
int ret;
switch (psp) {
@@ -215,6 +237,8 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_MAX:
if (power->axp20x_id == AXP813_ID)
return axp813_get_current_max(power, &val->intval);
+ else if (power->axp20x_id == AXP192_ID)
+ return axp192_get_current_max(power, &val->intval);
return axp20x_get_current_max(power, &val->intval);
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
@@ -256,16 +280,19 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_HEALTH_GOOD;
- if (power->axp20x_id == AXP202_ID) {
- ret = regmap_read(power->regmap,
- AXP20X_USB_OTG_STATUS, &v);
- if (ret)
- return ret;
+ if (power->axp20x_id == AXP192_ID)
+ reg = AXP192_USB_OTG_STATUS;
+ else if (power->axp20x_id == AXP202_ID)
+ reg = AXP20X_USB_OTG_STATUS;
+ else
+ break; /* Other chips lack the OTG status register */
- if (!(v & AXP20X_USB_STATUS_VBUS_VALID))
- val->intval =
- POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- }
+ ret = regmap_read(power->regmap, reg, &v);
+ if (ret)
+ return ret;
+
+ if (!(v & AXP20X_USB_STATUS_VBUS_VALID))
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
@@ -316,6 +343,28 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
return -EINVAL;
}
+static int axp192_usb_power_set_current_max(struct axp20x_usb_power *power,
+ int intval)
+{
+ const unsigned int mask = AXP192_VBUS_CLIMIT_EN |
+ AXP192_VBUS_CLIMIT_100mA;
+ unsigned int val;
+
+ switch (intval) {
+ case 100000:
+ val = AXP192_VBUS_CLIMIT_EN | AXP192_VBUS_CLIMIT_100mA;
+ break;
+ case 500000:
+ val = AXP192_VBUS_CLIMIT_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(power->regmap,
+ AXP20X_VBUS_IPSOUT_MGMT, mask, val);
+}
+
static int axp813_usb_power_set_current_max(struct axp20x_usb_power *power,
int intval)
{
@@ -383,6 +432,9 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
if (power->axp20x_id == AXP813_ID)
return axp813_usb_power_set_current_max(power,
val->intval);
+ else if (power->axp20x_id == AXP192_ID)
+ return axp192_usb_power_set_current_max(power,
+ val->intval);
return axp20x_usb_power_set_current_max(power, val->intval);
default:
@@ -468,6 +520,13 @@ struct axp_data {
enum axp20x_variants axp20x_id;
};
+static const struct axp_data axp192_data = {
+ .power_desc = &axp20x_usb_power_desc,
+ .irq_names = axp20x_irq_names,
+ .num_irq_names = ARRAY_SIZE(axp20x_irq_names),
+ .axp20x_id = AXP192_ID,
+};
+
static const struct axp_data axp202_data = {
.power_desc = &axp20x_usb_power_desc,
.irq_names = axp20x_irq_names,
@@ -600,7 +659,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (power->axp20x_id == AXP202_ID) {
+ if (power->axp20x_id == AXP192_ID || power->axp20x_id == AXP202_ID) {
/* Enable vbus valid checking */
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
AXP20X_VBUS_MON_VBUS_VALID,
@@ -659,6 +718,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
static const struct of_device_id axp20x_usb_power_match[] = {
{
+ .compatible = "x-powers,axp192-usb-power-supply",
+ .data = &axp192_data,
+ }, {
.compatible = "x-powers,axp202-usb-power-supply",
.data = &axp202_data,
}, {
--
2.38.0
The code may be clearer if parameters are not re-purposed to hold
temporary results like register values, so introduce local variables
as necessary to avoid that. Regroup macros based on chip type, and
use the FIELD_PREP() macro instead of a hand-rolled version.
Suggested-by: Jonathan Cameron <[email protected]>
Reviewed-by: Chen-Yu Tsai <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/iio/adc/axp20x_adc.c | 61 +++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 29 deletions(-)
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index 53bf7d4899d2..ab25e6e1ff65 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -5,6 +5,7 @@
* Quentin Schulz <[email protected]>
*/
+#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -22,20 +23,20 @@
#include <linux/mfd/axp20x.h>
#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
-
#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
+
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
-#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
-#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
-#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
-#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
+
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+
+#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
+#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
@@ -234,7 +235,7 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
- int size = 12;
+ int ret, size;
/*
* N.B.: Unlike the Chinese datasheets tell, the charging current is
@@ -246,10 +247,11 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev,
else
size = 12;
- *val = axp20x_read_variable_width(info->regmap, chan->address, size);
- if (*val < 0)
- return *val;
+ ret = axp20x_read_variable_width(info->regmap, chan->address, size);
+ if (ret < 0)
+ return ret;
+ *val = ret;
return IIO_VAL_INT;
}
@@ -257,11 +259,13 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ int ret;
- *val = axp20x_read_variable_width(info->regmap, chan->address, 12);
- if (*val < 0)
- return *val;
+ ret = axp20x_read_variable_width(info->regmap, chan->address, 12);
+ if (ret < 0)
+ return ret;
+ *val = ret;
return IIO_VAL_INT;
}
@@ -269,11 +273,13 @@ static int axp813_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ int ret;
- *val = axp20x_read_variable_width(info->regmap, chan->address, 12);
- if (*val < 0)
- return *val;
+ ret = axp20x_read_variable_width(info->regmap, chan->address, 12);
+ if (ret < 0)
+ return ret;
+ *val = ret;
return IIO_VAL_INT;
}
@@ -443,27 +449,27 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
int *val)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ unsigned int regval;
int ret;
- ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val);
+ ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®val);
if (ret < 0)
return ret;
switch (channel) {
case AXP20X_GPIO0_V:
- *val &= AXP20X_GPIO10_IN_RANGE_GPIO0;
+ regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO0, regval);
break;
case AXP20X_GPIO1_V:
- *val &= AXP20X_GPIO10_IN_RANGE_GPIO1;
+ regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO1, regval);
break;
default:
return -EINVAL;
}
- *val = *val ? 700000 : 0;
-
+ *val = regval ? 700000 : 0;
return IIO_VAL_INT;
}
@@ -548,7 +554,7 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
- unsigned int reg, regval;
+ unsigned int regmask, regval;
/*
* The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
@@ -560,25 +566,22 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
if (val != 0 && val != 700000)
return -EINVAL;
- val = val ? 1 : 0;
-
switch (chan->channel) {
case AXP20X_GPIO0_V:
- reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
- regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val);
+ regmask = AXP20X_GPIO10_IN_RANGE_GPIO0;
+ regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO0, !!val);
break;
case AXP20X_GPIO1_V:
- reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
- regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val);
+ regmask = AXP20X_GPIO10_IN_RANGE_GPIO1;
+ regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO1, !!val);
break;
default:
return -EINVAL;
}
- return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg,
- regval);
+ return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval);
}
static const struct iio_info axp20x_adc_iio_info = {
--
2.38.0
The AXP192's battery charger is similar to the others supported by
the axp20x_battery driver.
Acked-by: Rob Herring <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
.../power/supply/x-powers,axp20x-battery-power-supply.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
index e0b95ecbbebd..11f56b07e788 100644
--- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
+++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
@@ -20,6 +20,7 @@ allOf:
properties:
compatible:
oneOf:
+ - const: x-powers,axp192-battery-power-supply
- const: x-powers,axp202-battery-power-supply
- const: x-powers,axp209-battery-power-supply
- const: x-powers,axp221-battery-power-supply
--
2.38.0
The AXP192 PMIC is similar to the AXP202/AXP209, but with different
regulators, additional GPIOs, and a different IRQ register layout.
Acked-for-MFD-by: Lee Jones <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/axp20x-i2c.c | 2 +
drivers/mfd/axp20x.c | 141 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 84 ++++++++++++++++++++++
3 files changed, 227 insertions(+)
diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c
index 8fd6727dc30a..88d816adc69b 100644
--- a/drivers/mfd/axp20x-i2c.c
+++ b/drivers/mfd/axp20x-i2c.c
@@ -60,6 +60,7 @@ static void axp20x_i2c_remove(struct i2c_client *i2c)
#ifdef CONFIG_OF
static const struct of_device_id axp20x_i2c_of_match[] = {
{ .compatible = "x-powers,axp152", .data = (void *)AXP152_ID },
+ { .compatible = "x-powers,axp192", .data = (void *)AXP192_ID },
{ .compatible = "x-powers,axp202", .data = (void *)AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *)AXP209_ID },
{ .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
@@ -73,6 +74,7 @@ MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match);
static const struct i2c_device_id axp20x_i2c_id[] = {
{ "axp152", 0 },
+ { "axp192", 0 },
{ "axp202", 0 },
{ "axp209", 0 },
{ "axp221", 0 },
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 88a212a8168c..7d2949209d77 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -34,6 +34,7 @@
static const char * const axp20x_model_names[] = {
"AXP152",
+ "AXP192",
"AXP202",
"AXP209",
"AXP221",
@@ -92,6 +93,35 @@ static const struct regmap_access_table axp20x_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
+static const struct regmap_range axp192_writeable_ranges[] = {
+ regmap_reg_range(AXP192_DATACACHE(0), AXP192_DATACACHE(5)),
+ regmap_reg_range(AXP192_PWR_OUT_CTRL, AXP192_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP192_N_RSTO_CTRL),
+ regmap_reg_range(AXP20X_CC_CTRL, AXP20X_CC_CTRL),
+};
+
+static const struct regmap_range axp192_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP192_USB_OTG_STATUS),
+ regmap_reg_range(AXP192_IRQ1_STATE, AXP192_IRQ4_STATE),
+ regmap_reg_range(AXP192_IRQ5_STATE, AXP192_IRQ5_STATE),
+ regmap_reg_range(AXP20X_ACIN_V_ADC_H, AXP20X_IPSOUT_V_HIGH_L),
+ regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL),
+ regmap_reg_range(AXP192_GPIO2_0_STATE, AXP192_GPIO2_0_STATE),
+ regmap_reg_range(AXP192_GPIO4_3_STATE, AXP192_GPIO4_3_STATE),
+ regmap_reg_range(AXP192_N_RSTO_CTRL, AXP192_N_RSTO_CTRL),
+ regmap_reg_range(AXP20X_CHRG_CC_31_24, AXP20X_CC_CTRL),
+};
+
+static const struct regmap_access_table axp192_writeable_table = {
+ .yes_ranges = axp192_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp192_writeable_ranges),
+};
+
+static const struct regmap_access_table axp192_volatile_table = {
+ .yes_ranges = axp192_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp192_volatile_ranges),
+};
+
/* AXP22x ranges are shared with the AXP809, as they cover the same range */
static const struct regmap_range axp22x_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
@@ -173,6 +203,19 @@ static const struct resource axp152_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
};
+static const struct resource axp192_ac_power_supply_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"),
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"),
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_OVER_V, "ACIN_OVER_V"),
+};
+
+static const struct resource axp192_usb_power_supply_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_VALID, "VBUS_VALID"),
+ DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_NOT_VALID, "VBUS_NOT_VALID"),
+};
+
static const struct resource axp20x_ac_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"),
DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"),
@@ -245,6 +288,15 @@ static const struct regmap_config axp152_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct regmap_config axp192_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp192_writeable_table,
+ .volatile_table = &axp192_volatile_table,
+ .max_register = AXP20X_CC_CTRL,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static const struct regmap_config axp20x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -304,6 +356,42 @@ static const struct regmap_irq axp152_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP152, GPIO0_INPUT, 2, 0),
};
+static const struct regmap_irq axp192_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP192, ACIN_OVER_V, 0, 7),
+ INIT_REGMAP_IRQ(AXP192, ACIN_PLUGIN, 0, 6),
+ INIT_REGMAP_IRQ(AXP192, ACIN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP192, VBUS_OVER_V, 0, 4),
+ INIT_REGMAP_IRQ(AXP192, VBUS_PLUGIN, 0, 3),
+ INIT_REGMAP_IRQ(AXP192, VBUS_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP192, VBUS_V_LOW, 0, 1),
+ INIT_REGMAP_IRQ(AXP192, BATT_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP192, BATT_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP192, BATT_ENT_ACT_MODE, 1, 5),
+ INIT_REGMAP_IRQ(AXP192, BATT_EXIT_ACT_MODE, 1, 4),
+ INIT_REGMAP_IRQ(AXP192, CHARG, 1, 3),
+ INIT_REGMAP_IRQ(AXP192, CHARG_DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP192, BATT_TEMP_HIGH, 1, 1),
+ INIT_REGMAP_IRQ(AXP192, BATT_TEMP_LOW, 1, 0),
+ INIT_REGMAP_IRQ(AXP192, DIE_TEMP_HIGH, 2, 7),
+ INIT_REGMAP_IRQ(AXP192, CHARG_I_LOW, 2, 6),
+ INIT_REGMAP_IRQ(AXP192, DCDC1_V_LONG, 2, 5),
+ INIT_REGMAP_IRQ(AXP192, DCDC2_V_LONG, 2, 4),
+ INIT_REGMAP_IRQ(AXP192, DCDC3_V_LONG, 2, 3),
+ INIT_REGMAP_IRQ(AXP192, PEK_SHORT, 2, 1),
+ INIT_REGMAP_IRQ(AXP192, PEK_LONG, 2, 0),
+ INIT_REGMAP_IRQ(AXP192, N_OE_PWR_ON, 3, 7),
+ INIT_REGMAP_IRQ(AXP192, N_OE_PWR_OFF, 3, 6),
+ INIT_REGMAP_IRQ(AXP192, VBUS_VALID, 3, 5),
+ INIT_REGMAP_IRQ(AXP192, VBUS_NOT_VALID, 3, 4),
+ INIT_REGMAP_IRQ(AXP192, VBUS_SESS_VALID, 3, 3),
+ INIT_REGMAP_IRQ(AXP192, VBUS_SESS_END, 3, 2),
+ INIT_REGMAP_IRQ(AXP192, LOW_PWR_LVL, 3, 0),
+ INIT_REGMAP_IRQ(AXP192, TIMER, 4, 7),
+ INIT_REGMAP_IRQ(AXP192, GPIO2_INPUT, 4, 2),
+ INIT_REGMAP_IRQ(AXP192, GPIO1_INPUT, 4, 1),
+ INIT_REGMAP_IRQ(AXP192, GPIO0_INPUT, 4, 0),
+};
+
static const struct regmap_irq axp20x_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7),
INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6),
@@ -514,6 +602,32 @@ static const struct regmap_irq_chip axp152_regmap_irq_chip = {
.num_regs = 3,
};
+static unsigned int axp192_get_irq_reg(struct regmap_irq_chip_data *data,
+ unsigned int base, int index)
+{
+ /* linear mapping for IRQ1 to IRQ4 */
+ if (index < 4)
+ return base + index;
+
+ /* handle IRQ5 separately */
+ if (base == AXP192_IRQ1_EN)
+ return AXP192_IRQ5_EN;
+
+ return AXP192_IRQ5_STATE;
+}
+
+static const struct regmap_irq_chip axp192_regmap_irq_chip = {
+ .name = "axp192_irq_chip",
+ .status_base = AXP192_IRQ1_STATE,
+ .ack_base = AXP192_IRQ1_STATE,
+ .unmask_base = AXP192_IRQ1_EN,
+ .init_ack_masked = true,
+ .irqs = axp192_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp192_regmap_irqs),
+ .num_regs = 5,
+ .get_irq_reg = axp192_get_irq_reg,
+};
+
static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
.name = "axp20x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
@@ -588,6 +702,27 @@ static const struct regmap_irq_chip axp809_regmap_irq_chip = {
.num_regs = 5,
};
+static const struct mfd_cell axp192_cells[] = {
+ {
+ .name = "axp192-adc",
+ .of_compatible = "x-powers,axp192-adc",
+ }, {
+ .name = "axp20x-battery-power-supply",
+ .of_compatible = "x-powers,axp192-battery-power-supply",
+ }, {
+ .name = "axp20x-ac-power-supply",
+ .of_compatible = "x-powers,axp202-ac-power-supply",
+ .num_resources = ARRAY_SIZE(axp192_ac_power_supply_resources),
+ .resources = axp192_ac_power_supply_resources,
+ }, {
+ .name = "axp20x-usb-power-supply",
+ .of_compatible = "x-powers,axp192-usb-power-supply",
+ .num_resources = ARRAY_SIZE(axp192_usb_power_supply_resources),
+ .resources = axp192_usb_power_supply_resources,
+ },
+ { .name = "axp20x-regulator" },
+};
+
static const struct mfd_cell axp20x_cells[] = {
{
.name = "axp20x-gpio",
@@ -874,6 +1009,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_cfg = &axp152_regmap_config;
axp20x->regmap_irq_chip = &axp152_regmap_irq_chip;
break;
+ case AXP192_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp192_cells);
+ axp20x->cells = axp192_cells;
+ axp20x->regmap_cfg = &axp192_regmap_config;
+ axp20x->regmap_irq_chip = &axp192_regmap_irq_chip;
+ break;
case AXP202_ID:
case AXP209_ID:
axp20x->nr_cells = ARRAY_SIZE(axp20x_cells);
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 9ab0e2fca7ea..c9a9ee8ec246 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -12,6 +12,7 @@
enum axp20x_variants {
AXP152_ID = 0,
+ AXP192_ID,
AXP202_ID,
AXP209_ID,
AXP221_ID,
@@ -24,6 +25,7 @@ enum axp20x_variants {
NR_AXP20X_VARIANTS,
};
+#define AXP192_DATACACHE(m) (0x06 + (m))
#define AXP20X_DATACACHE(m) (0x04 + (m))
/* Power supply */
@@ -45,6 +47,13 @@ enum axp20x_variants {
#define AXP152_DCDC_FREQ 0x37
#define AXP152_DCDC_MODE 0x80
+#define AXP192_USB_OTG_STATUS 0x04
+#define AXP192_PWR_OUT_CTRL 0x12
+#define AXP192_DCDC2_V_OUT 0x23
+#define AXP192_DCDC1_V_OUT 0x26
+#define AXP192_DCDC3_V_OUT 0x27
+#define AXP192_LDO2_3_V_OUT 0x28
+
#define AXP20X_PWR_INPUT_STATUS 0x00
#define AXP20X_PWR_OP_MODE 0x01
#define AXP20X_USB_OTG_STATUS 0x02
@@ -139,6 +148,17 @@ enum axp20x_variants {
#define AXP152_IRQ2_STATE 0x49
#define AXP152_IRQ3_STATE 0x4a
+#define AXP192_IRQ1_EN 0x40
+#define AXP192_IRQ2_EN 0x41
+#define AXP192_IRQ3_EN 0x42
+#define AXP192_IRQ4_EN 0x43
+#define AXP192_IRQ1_STATE 0x44
+#define AXP192_IRQ2_STATE 0x45
+#define AXP192_IRQ3_STATE 0x46
+#define AXP192_IRQ4_STATE 0x47
+#define AXP192_IRQ5_EN 0x4a
+#define AXP192_IRQ5_STATE 0x4d
+
#define AXP20X_IRQ1_EN 0x40
#define AXP20X_IRQ2_EN 0x41
#define AXP20X_IRQ3_EN 0x42
@@ -153,6 +173,11 @@ enum axp20x_variants {
#define AXP20X_IRQ6_STATE 0x4d
/* ADC */
+#define AXP192_GPIO2_V_ADC_H 0x68
+#define AXP192_GPIO2_V_ADC_L 0x69
+#define AXP192_GPIO3_V_ADC_H 0x6a
+#define AXP192_GPIO3_V_ADC_L 0x6b
+
#define AXP20X_ACIN_V_ADC_H 0x56
#define AXP20X_ACIN_V_ADC_L 0x57
#define AXP20X_ACIN_I_ADC_H 0x58
@@ -182,6 +207,8 @@ enum axp20x_variants {
#define AXP20X_IPSOUT_V_HIGH_L 0x7f
/* Power supply */
+#define AXP192_GPIO30_IN_RANGE 0x85
+
#define AXP20X_DCDC_MODE 0x80
#define AXP20X_ADC_EN1 0x82
#define AXP20X_ADC_EN2 0x83
@@ -210,6 +237,16 @@ enum axp20x_variants {
#define AXP152_PWM1_FREQ_Y 0x9c
#define AXP152_PWM1_DUTY_CYCLE 0x9d
+#define AXP192_GPIO0_CTRL 0x90
+#define AXP192_LDO_IO0_V_OUT 0x91
+#define AXP192_GPIO1_CTRL 0x92
+#define AXP192_GPIO2_CTRL 0x93
+#define AXP192_GPIO2_0_STATE 0x94
+#define AXP192_GPIO4_3_CTRL 0x95
+#define AXP192_GPIO4_3_STATE 0x96
+#define AXP192_GPIO2_0_PULL 0x97
+#define AXP192_N_RSTO_CTRL 0x9e
+
#define AXP20X_GPIO0_CTRL 0x90
#define AXP20X_LDO5_V_OUT 0x91
#define AXP20X_GPIO1_CTRL 0x92
@@ -287,6 +324,17 @@ enum axp20x_variants {
#define AXP288_FG_TUNE5 0xed
/* Regulators IDs */
+enum {
+ AXP192_DCDC1 = 0,
+ AXP192_DCDC2,
+ AXP192_DCDC3,
+ AXP192_LDO1,
+ AXP192_LDO2,
+ AXP192_LDO3,
+ AXP192_LDO_IO0,
+ AXP192_REG_ID_MAX
+};
+
enum {
AXP20X_LDO1 = 0,
AXP20X_LDO2,
@@ -440,6 +488,42 @@ enum {
AXP152_IRQ_GPIO0_INPUT,
};
+enum axp192_irqs {
+ AXP192_IRQ_ACIN_OVER_V = 1,
+ AXP192_IRQ_ACIN_PLUGIN,
+ AXP192_IRQ_ACIN_REMOVAL,
+ AXP192_IRQ_VBUS_OVER_V,
+ AXP192_IRQ_VBUS_PLUGIN,
+ AXP192_IRQ_VBUS_REMOVAL,
+ AXP192_IRQ_VBUS_V_LOW,
+ AXP192_IRQ_BATT_PLUGIN,
+ AXP192_IRQ_BATT_REMOVAL,
+ AXP192_IRQ_BATT_ENT_ACT_MODE,
+ AXP192_IRQ_BATT_EXIT_ACT_MODE,
+ AXP192_IRQ_CHARG,
+ AXP192_IRQ_CHARG_DONE,
+ AXP192_IRQ_BATT_TEMP_HIGH,
+ AXP192_IRQ_BATT_TEMP_LOW,
+ AXP192_IRQ_DIE_TEMP_HIGH,
+ AXP192_IRQ_CHARG_I_LOW,
+ AXP192_IRQ_DCDC1_V_LONG,
+ AXP192_IRQ_DCDC2_V_LONG,
+ AXP192_IRQ_DCDC3_V_LONG,
+ AXP192_IRQ_PEK_SHORT = 22,
+ AXP192_IRQ_PEK_LONG,
+ AXP192_IRQ_N_OE_PWR_ON,
+ AXP192_IRQ_N_OE_PWR_OFF,
+ AXP192_IRQ_VBUS_VALID,
+ AXP192_IRQ_VBUS_NOT_VALID,
+ AXP192_IRQ_VBUS_SESS_VALID,
+ AXP192_IRQ_VBUS_SESS_END,
+ AXP192_IRQ_LOW_PWR_LVL = 31,
+ AXP192_IRQ_TIMER,
+ AXP192_IRQ_GPIO2_INPUT = 37,
+ AXP192_IRQ_GPIO1_INPUT,
+ AXP192_IRQ_GPIO0_INPUT,
+};
+
enum {
AXP20X_IRQ_ACIN_OVER_V = 1,
AXP20X_IRQ_ACIN_PLUGIN,
--
2.38.0
The AXP192 is identical to the AXP20x, except for the addition of
two more GPIO ADC channels.
Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/iio/adc/axp20x_adc.c | 279 +++++++++++++++++++++++++++++++++++
1 file changed, 279 insertions(+)
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index 75bda94dbce1..8d748b814f20 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -22,11 +22,19 @@
#include <linux/iio/machine.h>
#include <linux/mfd/axp20x.h>
+#define AXP192_ADC_EN1_MASK GENMASK(7, 0)
+#define AXP192_ADC_EN2_MASK (GENMASK(3, 0) | BIT(7))
+
#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
+#define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0)
+#define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1)
+#define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2)
+#define AXP192_GPIO30_IN_RANGE_GPIO3 BIT(3)
+
#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
@@ -71,6 +79,25 @@ struct axp20x_adc_iio {
const struct axp_data *data;
};
+enum axp192_adc_channel_v {
+ AXP192_ACIN_V = 0,
+ AXP192_VBUS_V,
+ AXP192_TS_IN,
+ AXP192_GPIO0_V,
+ AXP192_GPIO1_V,
+ AXP192_GPIO2_V,
+ AXP192_GPIO3_V,
+ AXP192_IPSOUT_V,
+ AXP192_BATT_V,
+};
+
+enum axp192_adc_channel_i {
+ AXP192_ACIN_I = 0,
+ AXP192_VBUS_I,
+ AXP192_BATT_CHRG_I,
+ AXP192_BATT_DISCHRG_I,
+};
+
enum axp20x_adc_channel_v {
AXP20X_ACIN_V = 0,
AXP20X_VBUS_V,
@@ -158,6 +185,43 @@ static struct iio_map axp22x_maps[] = {
* The only exception is for the battery. batt_v will be in_voltage6_raw and
* charge current in_current6_raw and discharge current will be in_current7_raw.
*/
+static const struct iio_chan_spec axp192_adc_channels[] = {
+ AXP20X_ADC_CHANNEL(AXP192_ACIN_V, "acin_v", IIO_VOLTAGE,
+ AXP20X_ACIN_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP192_ACIN_I, "acin_i", IIO_CURRENT,
+ AXP20X_ACIN_I_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP192_VBUS_V, "vbus_v", IIO_VOLTAGE,
+ AXP20X_VBUS_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP192_VBUS_I, "vbus_i", IIO_CURRENT,
+ AXP20X_VBUS_I_ADC_H),
+ {
+ .type = IIO_TEMP,
+ .address = AXP20X_TEMP_ADC_H,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "pmic_temp",
+ },
+ AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
+ AXP20X_GPIO0_V_ADC_H),
+ AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
+ AXP20X_GPIO1_V_ADC_H),
+ AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO2_V, "gpio2_v", IIO_VOLTAGE,
+ AXP192_GPIO2_V_ADC_H),
+ AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO3_V, "gpio3_v", IIO_VOLTAGE,
+ AXP192_GPIO3_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP192_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
+ AXP20X_IPSOUT_V_HIGH_H),
+ AXP20X_ADC_CHANNEL(AXP192_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP20X_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP192_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP20X_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP192_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+ AXP20X_BATT_DISCHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP192_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP20X_TS_IN_H),
+};
+
static const struct iio_chan_spec axp20x_adc_channels[] = {
AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
AXP20X_ACIN_V_ADC_H),
@@ -231,6 +295,27 @@ static const struct iio_chan_spec axp813_adc_channels[] = {
AXP288_TS_ADC_H),
};
+static int axp192_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ int ret, size;
+
+ if (chan->type == IIO_CURRENT &&
+ (chan->channel == AXP192_BATT_CHRG_I ||
+ chan->channel == AXP192_BATT_DISCHRG_I))
+ size = 13;
+ else
+ size = 12;
+
+ ret = axp20x_read_variable_width(info->regmap, chan->address, size);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+}
+
static int axp20x_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -283,6 +368,44 @@ static int axp813_adc_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
+static int axp192_adc_scale_voltage(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP192_ACIN_V:
+ case AXP192_VBUS_V:
+ *val = 1;
+ *val2 = 700000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP192_GPIO0_V:
+ case AXP192_GPIO1_V:
+ case AXP192_GPIO2_V:
+ case AXP192_GPIO3_V:
+ *val = 0;
+ *val2 = 500000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP192_BATT_V:
+ *val = 1;
+ *val2 = 100000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP192_IPSOUT_V:
+ *val = 1;
+ *val2 = 400000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP192_TS_IN:
+ /* 0.8 mV per LSB */
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
{
switch (channel) {
@@ -386,6 +509,29 @@ static int axp20x_adc_scale_current(int channel, int *val, int *val2)
}
}
+static int axp192_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp192_adc_scale_voltage(chan->channel, val, val2);
+
+ case IIO_CURRENT:
+ /*
+ * AXP192 current channels are identical to the AXP20x,
+ * therefore we can re-use the scaling function.
+ */
+ return axp20x_adc_scale_current(chan->channel, val, val2);
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
int *val2)
{
@@ -445,6 +591,42 @@ static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
}
}
+static int axp192_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
+ int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(info->regmap, AXP192_GPIO30_IN_RANGE, ®val);
+ if (ret < 0)
+ return ret;
+
+ switch (channel) {
+ case AXP192_GPIO0_V:
+ regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO0, regval);
+ break;
+
+ case AXP192_GPIO1_V:
+ regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO1, regval);
+ break;
+
+ case AXP192_GPIO2_V:
+ regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO2, regval);
+ break;
+
+ case AXP192_GPIO3_V:
+ regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO3, regval);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ *val = regval ? 700000 : 0;
+ return IIO_VAL_INT;
+}
+
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
int *val)
{
@@ -473,6 +655,22 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
return IIO_VAL_INT;
}
+static int axp192_adc_offset(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp192_adc_offset_voltage(indio_dev, chan->channel, val);
+
+ case IIO_TEMP:
+ *val = -1447;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_adc_offset(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -489,6 +687,25 @@ static int axp20x_adc_offset(struct iio_dev *indio_dev,
}
}
+static int axp192_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_OFFSET:
+ return axp192_adc_offset(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ return axp192_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp192_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -549,6 +766,51 @@ static int axp813_read_raw(struct iio_dev *indio_dev,
}
}
+static int axp192_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ unsigned int regmask, regval;
+
+ /*
+ * The AXP192 PMIC allows the user to choose between 0V and 0.7V offsets
+ * for (independently) GPIO0-3 when in ADC mode.
+ */
+ if (mask != IIO_CHAN_INFO_OFFSET)
+ return -EINVAL;
+
+ if (val != 0 && val != 700000)
+ return -EINVAL;
+
+ switch (chan->channel) {
+ case AXP192_GPIO0_V:
+ regmask = AXP192_GPIO30_IN_RANGE_GPIO0;
+ regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO0, !!val);
+ break;
+
+ case AXP192_GPIO1_V:
+ regmask = AXP192_GPIO30_IN_RANGE_GPIO1;
+ regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO1, !!val);
+ break;
+
+ case AXP192_GPIO2_V:
+ regmask = AXP192_GPIO30_IN_RANGE_GPIO2;
+ regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO2, !!val);
+ break;
+
+ case AXP192_GPIO3_V:
+ regmask = AXP192_GPIO30_IN_RANGE_GPIO3;
+ regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO3, !!val);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(info->regmap, AXP192_GPIO30_IN_RANGE, regmask, regval);
+}
+
static int axp20x_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
@@ -584,6 +846,11 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval);
}
+static const struct iio_info axp192_adc_iio_info = {
+ .read_raw = axp192_read_raw,
+ .write_raw = axp192_write_raw,
+};
+
static const struct iio_info axp20x_adc_iio_info = {
.read_raw = axp20x_read_raw,
.write_raw = axp20x_write_raw,
@@ -629,6 +896,16 @@ struct axp_data {
struct iio_map *maps;
};
+static const struct axp_data axp192_data = {
+ .iio_info = &axp192_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp192_adc_channels),
+ .channels = axp192_adc_channels,
+ .adc_en1_mask = AXP192_ADC_EN1_MASK,
+ .adc_en2_mask = AXP192_ADC_EN2_MASK,
+ .adc_rate = axp20x_adc_rate,
+ .maps = axp20x_maps,
+};
+
static const struct axp_data axp20x_data = {
.iio_info = &axp20x_adc_iio_info,
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
@@ -658,6 +935,7 @@ static const struct axp_data axp813_data = {
};
static const struct of_device_id axp20x_adc_of_match[] = {
+ { .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, },
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
@@ -666,6 +944,7 @@ static const struct of_device_id axp20x_adc_of_match[] = {
MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
static const struct platform_device_id axp20x_adc_id_match[] = {
+ { .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, },
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
--
2.38.0
Add a table-based lookup method for constant charge current,
which is necessary when the setting cannot be represented as
a linear range.
This also replaces the hard-coded 300 mA default ccc setting
if the DT-specified value is unsupported; the minimum value
for the device is now set exactly instead of relying on the
value being rounded down to a supported value.
Reviewed-by: Andy Shevchenko <[email protected]>
Acked-by: Sebastian Reichel <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/power/supply/axp20x_battery.c | 59 +++++++++++++++++++++------
1 file changed, 47 insertions(+), 12 deletions(-)
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 9106077c0dbb..401779d84d2b 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -61,6 +61,14 @@ struct axp20x_batt_ps;
struct axp_data {
int ccc_scale;
int ccc_offset;
+ /*
+ * Lookup table for constant charge current, if provided this is used
+ * instead of ccc_scale/ccc_offset.
+ *
+ * The table is indexed by the field AXP20X_CHRG_CTRL1_TGT_CURR so it
+ * must have AXP20X_CHRG_CTRL1_TGT_CURR+1 elements.
+ */
+ const int *ccc_table;
bool has_fg_valid;
int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
@@ -176,7 +184,10 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
*val &= AXP20X_CHRG_CTRL1_TGT_CURR;
- *val = *val * axp->data->ccc_scale + axp->data->ccc_offset;
+ if (axp->data->ccc_table)
+ *val = axp->data->ccc_table[*val];
+ else
+ *val = *val * axp->data->ccc_scale + axp->data->ccc_offset;
return 0;
}
@@ -389,16 +400,35 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
AXP20X_CHRG_CTRL1_TGT_VOLT, val);
}
+static int axp20x_get_constant_charge_current_sel(struct axp20x_batt_ps *axp_batt,
+ int charge_current)
+{
+ int i;
+
+ if (axp_batt->data->ccc_table) {
+ for (i = AXP20X_CHRG_CTRL1_TGT_CURR; i >= 0; i--) {
+ if (axp_batt->data->ccc_table[i] <= charge_current)
+ return i;
+ }
+
+ return -EINVAL;
+ }
+
+ i = (charge_current - axp_batt->data->ccc_offset) / axp_batt->data->ccc_scale;
+ if (i > AXP20X_CHRG_CTRL1_TGT_CURR || i < 0)
+ return -EINVAL;
+
+ return i;
+}
+
static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
int charge_current)
{
if (charge_current > axp_batt->max_ccc)
return -EINVAL;
- charge_current = (charge_current - axp_batt->data->ccc_offset) /
- axp_batt->data->ccc_scale;
-
- if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
+ charge_current = axp20x_get_constant_charge_current_sel(axp_batt, charge_current);
+ if (charge_current < 0)
return -EINVAL;
return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1,
@@ -410,14 +440,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
{
bool lower_max = false;
- charge_current = (charge_current - axp->data->ccc_offset) /
- axp->data->ccc_scale;
-
- if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
+ charge_current = axp20x_get_constant_charge_current_sel(axp, charge_current);
+ if (charge_current < 0)
return -EINVAL;
- charge_current = charge_current * axp->data->ccc_scale +
- axp->data->ccc_offset;
+ if (axp->data->ccc_table)
+ charge_current = axp->data->ccc_table[charge_current];
+ else
+ charge_current = charge_current * axp->data->ccc_scale + axp->data->ccc_offset;
if (charge_current > axp->max_ccc)
dev_warn(axp->dev,
@@ -629,7 +659,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
ccc)) {
dev_err(&pdev->dev,
"couldn't set constant charge current from DT: fallback to minimum value\n");
- ccc = 300000;
+
+ if (axp20x_batt->data->ccc_table)
+ ccc = axp20x_batt->data->ccc_table[0];
+ else
+ ccc = axp20x_batt->data->ccc_offset;
+
axp20x_batt->max_ccc = ccc;
axp20x_set_constant_charge_current(axp20x_batt, ccc);
}
--
2.38.0
Add a "has_fg" flag to indicate that the chip has a fuel gauge.
Report battery full status on chips with no fuel gauge using the
battery voltage.
Acked-by: Sebastian Reichel <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/power/supply/axp20x_battery.c | 34 ++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 401779d84d2b..574c1d001556 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -69,6 +69,7 @@ struct axp_data {
* must have AXP20X_CHRG_CTRL1_TGT_CURR+1 elements.
*/
const int *ccc_table;
+ bool has_fg;
bool has_fg_valid;
int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
@@ -197,7 +198,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
union power_supply_propval *val)
{
struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
- int ret = 0, reg, val1;
+ int ret = 0, reg, val1, val2;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
@@ -231,6 +232,34 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
return 0;
}
+ /*
+ * If the chip does not have a fuel gauge, we check battery full status
+ * using voltage instead.
+ */
+ if (!axp20x_batt->data->has_fg) {
+ ret = axp20x_batt->data->get_max_voltage(axp20x_batt, &val1);
+ if (ret)
+ return ret;
+
+ ret = iio_read_channel_processed(axp20x_batt->batt_v, &val2);
+ if (ret)
+ return ret;
+
+ /* IIO subsystem reports voltage in mV but we need uV */
+ val2 *= 1000;
+
+ /*
+ * According to the AXP192 datasheet, charging will restart if
+ * the battery voltage drops below V_rch = V_tgt - 0.1 V, so we
+ * report the battery is full if its voltage is at least V_rch.
+ */
+ if (val2 >= val1 - 100000)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ }
+
ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1);
if (ret)
return ret;
@@ -552,6 +581,7 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
static const struct axp_data axp209_data = {
.ccc_scale = 100000,
.ccc_offset = 300000,
+ .has_fg = true,
.get_max_voltage = axp20x_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
};
@@ -559,6 +589,7 @@ static const struct axp_data axp209_data = {
static const struct axp_data axp221_data = {
.ccc_scale = 150000,
.ccc_offset = 300000,
+ .has_fg = true,
.has_fg_valid = true,
.get_max_voltage = axp22x_battery_get_max_voltage,
.set_max_voltage = axp22x_battery_set_max_voltage,
@@ -567,6 +598,7 @@ static const struct axp_data axp221_data = {
static const struct axp_data axp813_data = {
.ccc_scale = 200000,
.ccc_offset = 200000,
+ .has_fg = true,
.has_fg_valid = true,
.get_max_voltage = axp813_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
--
2.38.0
Add support for the AXP192 PMIC.
Acked-by: Mark Brown <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/regulator/axp20x-regulator.c | 100 ++++++++++++++++++++++++---
1 file changed, 91 insertions(+), 9 deletions(-)
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index d260c442b788..ee79a9ed0249 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -27,6 +27,29 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
+#define AXP192_GPIO0_FUNC_MASK GENMASK(2, 0)
+
+#define AXP192_IO_ENABLED 0x02
+#define AXP192_IO_DISABLED 0x06
+
+#define AXP192_WORKMODE_DCDC1_MASK BIT_MASK(3)
+#define AXP192_WORKMODE_DCDC2_MASK BIT_MASK(2)
+#define AXP192_WORKMODE_DCDC3_MASK BIT_MASK(1)
+
+#define AXP192_DCDC1_V_OUT_MASK GENMASK(6, 0)
+#define AXP192_DCDC2_V_OUT_MASK GENMASK(5, 0)
+#define AXP192_DCDC3_V_OUT_MASK GENMASK(6, 0)
+#define AXP192_LDO2_V_OUT_MASK GENMASK(7, 4)
+#define AXP192_LDO3_V_OUT_MASK GENMASK(3, 0)
+#define AXP192_LDO_IO0_V_OUT_MASK GENMASK(7, 4)
+
+#define AXP192_PWR_OUT_EXTEN_MASK BIT_MASK(6)
+#define AXP192_PWR_OUT_DCDC2_MASK BIT_MASK(4)
+#define AXP192_PWR_OUT_LDO3_MASK BIT_MASK(3)
+#define AXP192_PWR_OUT_LDO2_MASK BIT_MASK(2)
+#define AXP192_PWR_OUT_DCDC3_MASK BIT_MASK(1)
+#define AXP192_PWR_OUT_DCDC1_MASK BIT_MASK(0)
+
#define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0)
#define AXP20X_GPIO1_FUNC_MASK GENMASK(3, 0)
@@ -375,25 +398,32 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp)
switch (axp20x->variant) {
case AXP209_ID:
- if (id == AXP20X_DCDC2) {
+ if (id == AXP20X_LDO3) {
slew_rates = axp209_dcdc2_ldo3_slew_rates;
rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates);
reg = AXP20X_DCDC2_LDO3_V_RAMP;
- mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK |
- AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
+ mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK |
+ AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
enable = (ramp > 0) ?
- AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : 0;
+ AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : 0;
break;
}
- if (id == AXP20X_LDO3) {
+ fallthrough;
+
+ case AXP192_ID:
+ /*
+ * AXP192 and AXP209 share the same DCDC2 ramp configuration
+ */
+ if ((axp20x->variant == AXP209_ID && id == AXP20X_DCDC2) ||
+ (axp20x->variant == AXP192_ID && id == AXP192_DCDC2)) {
slew_rates = axp209_dcdc2_ldo3_slew_rates;
rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates);
reg = AXP20X_DCDC2_LDO3_V_RAMP;
- mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK |
- AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
+ mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK |
+ AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
enable = (ramp > 0) ?
- AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : 0;
+ AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : 0;
break;
}
@@ -415,7 +445,8 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp)
if (ramp > slew_rates[i])
break;
- if (id == AXP20X_DCDC2)
+ if ((axp20x->variant == AXP209_ID && id == AXP20X_DCDC2) ||
+ (axp20x->variant == AXP192_ID && id == AXP192_DCDC2))
cfg = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(i);
else
cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i);
@@ -511,6 +542,29 @@ static const struct regulator_ops axp20x_ops_sw = {
.is_enabled = regulator_is_enabled_regmap,
};
+static const struct regulator_desc axp192_regulators[] = {
+ AXP_DESC(AXP192, DCDC1, "dcdc1", "vin1", 700, 3500, 25,
+ AXP192_DCDC1_V_OUT, AXP192_DCDC1_V_OUT_MASK,
+ AXP192_PWR_OUT_CTRL, AXP192_PWR_OUT_DCDC1_MASK),
+ AXP_DESC(AXP192, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+ AXP192_DCDC2_V_OUT, AXP192_DCDC2_V_OUT_MASK,
+ AXP192_PWR_OUT_CTRL, AXP192_PWR_OUT_DCDC2_MASK),
+ AXP_DESC(AXP192, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+ AXP192_DCDC3_V_OUT, AXP192_DCDC3_V_OUT_MASK,
+ AXP192_PWR_OUT_CTRL, AXP192_PWR_OUT_DCDC3_MASK),
+ AXP_DESC_FIXED(AXP192, LDO1, "ldo1", "acin", 1250),
+ AXP_DESC(AXP192, LDO2, "ldo2", "ldoin", 1800, 3300, 100,
+ AXP192_LDO2_3_V_OUT, AXP192_LDO2_V_OUT_MASK,
+ AXP192_PWR_OUT_CTRL, AXP192_PWR_OUT_LDO2_MASK),
+ AXP_DESC(AXP192, LDO3, "ldo3", "ldoin", 1800, 3300, 100,
+ AXP192_LDO2_3_V_OUT, AXP192_LDO3_V_OUT_MASK,
+ AXP192_PWR_OUT_CTRL, AXP192_PWR_OUT_LDO3_MASK),
+ AXP_DESC_IO(AXP192, LDO_IO0, "ldo_io0", "ips", 700, 3300, 100,
+ AXP192_LDO_IO0_V_OUT, AXP192_LDO_IO0_V_OUT_MASK,
+ AXP192_GPIO0_CTRL, AXP192_GPIO0_FUNC_MASK,
+ AXP192_IO_ENABLED, AXP192_IO_DISABLED),
+};
+
static const struct linear_range axp20x_ldo4_ranges[] = {
REGULATOR_LINEAR_RANGE(1250000,
AXP20X_LDO4_V_OUT_1250mV_START,
@@ -1008,6 +1062,12 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
u32 min, max, def, step;
switch (axp20x->variant) {
+ case AXP192_ID:
+ min = 900;
+ max = 2025;
+ def = 1500;
+ step = 75;
+ break;
case AXP202_ID:
case AXP209_ID:
min = 750;
@@ -1100,6 +1160,24 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work
unsigned int mask;
switch (axp20x->variant) {
+ case AXP192_ID:
+ switch (id) {
+ case AXP192_DCDC1:
+ mask = AXP192_WORKMODE_DCDC1_MASK;
+ break;
+ case AXP192_DCDC2:
+ mask = AXP192_WORKMODE_DCDC2_MASK;
+ break;
+ case AXP192_DCDC3:
+ mask = AXP192_WORKMODE_DCDC3_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ workmode <<= ffs(mask) - 1;
+ break;
+
case AXP202_ID:
case AXP209_ID:
if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
@@ -1220,6 +1298,10 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
bool drivevbus = false;
switch (axp20x->variant) {
+ case AXP192_ID:
+ regulators = axp192_regulators;
+ nregulators = AXP192_REG_ID_MAX;
+ break;
case AXP202_ID:
case AXP209_ID:
regulators = axp20x_regulators;
--
2.38.0
The adc_en2 flag is essentially specific to axp20x-compatible devices
because it hardcodes register values. Replace it with a mask field
so the register value can be specified in device match data.
Reviewed-by: Andy Shevchenko <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/iio/adc/axp20x_adc.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index ab25e6e1ff65..75bda94dbce1 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -623,9 +623,9 @@ struct axp_data {
int num_channels;
struct iio_chan_spec const *channels;
unsigned long adc_en1_mask;
+ unsigned long adc_en2_mask;
int (*adc_rate)(struct axp20x_adc_iio *info,
int rate);
- bool adc_en2;
struct iio_map *maps;
};
@@ -634,8 +634,8 @@ static const struct axp_data axp20x_data = {
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
.channels = axp20x_adc_channels,
.adc_en1_mask = AXP20X_ADC_EN1_MASK,
+ .adc_en2_mask = AXP20X_ADC_EN2_MASK,
.adc_rate = axp20x_adc_rate,
- .adc_en2 = true,
.maps = axp20x_maps,
};
@@ -645,7 +645,6 @@ static const struct axp_data axp22x_data = {
.channels = axp22x_adc_channels,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp22x_adc_rate,
- .adc_en2 = false,
.maps = axp22x_maps,
};
@@ -655,7 +654,6 @@ static const struct axp_data axp813_data = {
.channels = axp813_adc_channels,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp813_adc_rate,
- .adc_en2 = false,
.maps = axp22x_maps,
};
@@ -713,10 +711,10 @@ static int axp20x_probe(struct platform_device *pdev)
/* Enable the ADCs on IP */
regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
- if (info->data->adc_en2)
- /* Enable GPIO0/1 and internal temperature ADCs */
+ if (info->data->adc_en2_mask)
regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
- AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
+ info->data->adc_en2_mask,
+ info->data->adc_en2_mask);
/* Configure ADCs rate */
info->data->adc_rate(info, 100);
@@ -741,7 +739,7 @@ static int axp20x_probe(struct platform_device *pdev)
fail_map:
regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
- if (info->data->adc_en2)
+ if (info->data->adc_en2_mask)
regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
return ret;
@@ -757,7 +755,7 @@ static int axp20x_remove(struct platform_device *pdev)
regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
- if (info->data->adc_en2)
+ if (info->data->adc_en2_mask)
regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
return 0;
--
2.38.0
Aidan MacDonald <[email protected]> writes:
> This series adds support for the AXP192 PMIC to the AXP20x MFD driver
> framework, including support for regulators, ADCs, and AC/USB/battery
> power supplies.
>
> v6 is a resend of v5 from July -- the patches haven't changed at all
> but I've rebased them on the latest git master branch.
Whoops, forgot the link to v5. Here it is:
https://lore.kernel.org/linux-iio/[email protected]/
The AXP192 has a battery charger similar to other X-Powers PMICs,
but unlike the other supported devices, it does not have a fuel
gauge and can't report battery capacity directly.
Acked-by: Sebastian Reichel <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/power/supply/axp20x_battery.c | 49 +++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 574c1d001556..1e84d26ce8e3 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -544,6 +544,19 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
}
}
+static enum power_supply_property axp192_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+};
+
static enum power_supply_property axp20x_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
@@ -568,6 +581,16 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
}
+static const struct power_supply_desc axp192_batt_ps_desc = {
+ .name = "axp192-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = axp192_battery_props,
+ .num_properties = ARRAY_SIZE(axp192_battery_props),
+ .property_is_writeable = axp20x_battery_prop_writeable,
+ .get_property = axp20x_battery_get_prop,
+ .set_property = axp20x_battery_set_prop,
+};
+
static const struct power_supply_desc axp20x_batt_ps_desc = {
.name = "axp20x-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
@@ -578,6 +601,19 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
.set_property = axp20x_battery_set_prop,
};
+static const int axp192_ccc_table[AXP20X_CHRG_CTRL1_TGT_CURR+1] = {
+ 100000, 190000, 280000, 360000,
+ 450000, 550000, 630000, 700000,
+ 780000, 880000, 960000, 1000000,
+ 1080000, 1160000, 1240000, 1320000,
+};
+
+static const struct axp_data axp192_data = {
+ .ccc_table = axp192_ccc_table,
+ .get_max_voltage = axp20x_battery_get_max_voltage,
+ .set_max_voltage = axp20x_battery_set_max_voltage,
+};
+
static const struct axp_data axp209_data = {
.ccc_scale = 100000,
.ccc_offset = 300000,
@@ -606,6 +642,9 @@ static const struct axp_data axp813_data = {
static const struct of_device_id axp20x_battery_ps_id[] = {
{
+ .compatible = "x-powers,axp192-battery-power-supply",
+ .data = (void *)&axp192_data,
+ }, {
.compatible = "x-powers,axp209-battery-power-supply",
.data = (void *)&axp209_data,
}, {
@@ -623,6 +662,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
struct axp20x_batt_ps *axp20x_batt;
struct power_supply_config psy_cfg = {};
struct power_supply_battery_info *info;
+ const struct power_supply_desc *ps_desc;
struct device *dev = &pdev->dev;
if (!of_device_is_available(pdev->dev.of_node))
@@ -666,9 +706,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
- axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
- &axp20x_batt_ps_desc,
- &psy_cfg);
+ if (!axp20x_batt->data->has_fg)
+ ps_desc = &axp192_batt_ps_desc;
+ else
+ ps_desc = &axp20x_batt_ps_desc;
+
+ axp20x_batt->batt = devm_power_supply_register(&pdev->dev, ps_desc, &psy_cfg);
if (IS_ERR(axp20x_batt->batt)) {
dev_err(&pdev->dev, "failed to register power supply: %ld\n",
PTR_ERR(axp20x_batt->batt));
--
2.38.0
On Mon, 17 Oct 2022 00:43:22 +0100
Aidan MacDonald <[email protected]> wrote:
> This series adds support for the AXP192 PMIC to the AXP20x MFD driver
> framework, including support for regulators, ADCs, and AC/USB/battery
> power supplies.
>
> v6 is a resend of v5 from July -- the patches haven't changed at all
> but I've rebased them on the latest git master branch.
Hi Aidan,
Lee has recently expressed that he keen to take as much of these sorts
of series as possible via the various subsystem trees.
As such, it is useful to call out in the cover letter of such a series
if this can be done. For example, patch 9 (last IIO one) can't be
applied without defines in patch 6 (I think). Thus I'm assuming Lee
will do an immutable branch with at least those patches on it.
Perhaps worth expressing if that is also the case for the power
and regulator subsystem patches?
Thanks,
Jonathan
>
> Aidan MacDonald (13):
> dt-bindings: mfd: add bindings for AXP192 MFD device
> dt-bindings: iio: adc: axp209: Add AXP192 compatible
> dt-bindings: power: supply: axp20x: Add AXP192 compatible
> dt-bindings: power: axp20x-battery: Add AXP192 compatible
> mfd: axp20x: Add support for AXP192
> regulator: axp20x: Add support for AXP192
> iio: adc: axp20x_adc: Minor code cleanups
> iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field
> iio: adc: axp20x_adc: Add support for AXP192
> power: supply: axp20x_usb_power: Add support for AXP192
> power: axp20x_battery: Add constant charge current table
> power: axp20x_battery: Support battery status without fuel gauge
> power: axp20x_battery: Add support for AXP192
>
> .../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 +
> .../bindings/mfd/x-powers,axp152.yaml | 1 +
> .../x-powers,axp20x-battery-power-supply.yaml | 1 +
> .../x-powers,axp20x-usb-power-supply.yaml | 1 +
> drivers/iio/adc/axp20x_adc.c | 356 ++++++++++++++++--
> drivers/mfd/axp20x-i2c.c | 2 +
> drivers/mfd/axp20x.c | 141 +++++++
> drivers/power/supply/axp20x_battery.c | 142 ++++++-
> drivers/power/supply/axp20x_usb_power.c | 84 ++++-
> drivers/regulator/axp20x-regulator.c | 100 ++++-
> include/linux/mfd/axp20x.h | 84 +++++
> 11 files changed, 856 insertions(+), 74 deletions(-)
>
Jonathan Cameron <[email protected]> writes:
> On Mon, 17 Oct 2022 00:43:22 +0100
> Aidan MacDonald <[email protected]> wrote:
>
>> This series adds support for the AXP192 PMIC to the AXP20x MFD driver
>> framework, including support for regulators, ADCs, and AC/USB/battery
>> power supplies.
>>
>> v6 is a resend of v5 from July -- the patches haven't changed at all
>> but I've rebased them on the latest git master branch.
>
> Hi Aidan,
>
> Lee has recently expressed that he keen to take as much of these sorts
> of series as possible via the various subsystem trees.
>
> As such, it is useful to call out in the cover letter of such a series
> if this can be done. For example, patch 9 (last IIO one) can't be
> applied without defines in patch 6 (I think). Thus I'm assuming Lee
> will do an immutable branch with at least those patches on it.
>
> Perhaps worth expressing if that is also the case for the power
> and regulator subsystem patches?
>
> Thanks,
>
> Jonathan
Yep, the IIO, regulator, and power subsystem patches all depend on
the MFD patch. Specifically, patches 6, 9, and 10 depend on patch 5.
I can't get rid of this dependency because the variant ID (AXP192_ID)
has to be defined centrally in the MFD patch.
The axp20x_battery patches (last three of the whole series) don't
depend on the variant ID or other defines, so they could be taken
independently through the power subsystem.
Even though the IIO cleanups (7 and 8) don't depend on anything else
I imagine it'd cause problems to take those via IIO because patch 9
depends on them.
IOW: Lee probably needs to take patches 5-10.
- Aidan
>>
>> Aidan MacDonald (13):
>> dt-bindings: mfd: add bindings for AXP192 MFD device
>> dt-bindings: iio: adc: axp209: Add AXP192 compatible
>> dt-bindings: power: supply: axp20x: Add AXP192 compatible
>> dt-bindings: power: axp20x-battery: Add AXP192 compatible
>> mfd: axp20x: Add support for AXP192
>> regulator: axp20x: Add support for AXP192
>> iio: adc: axp20x_adc: Minor code cleanups
>> iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field
>> iio: adc: axp20x_adc: Add support for AXP192
>> power: supply: axp20x_usb_power: Add support for AXP192
>> power: axp20x_battery: Add constant charge current table
>> power: axp20x_battery: Support battery status without fuel gauge
>> power: axp20x_battery: Add support for AXP192
>>
>> .../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 +
>> .../bindings/mfd/x-powers,axp152.yaml | 1 +
>> .../x-powers,axp20x-battery-power-supply.yaml | 1 +
>> .../x-powers,axp20x-usb-power-supply.yaml | 1 +
>> drivers/iio/adc/axp20x_adc.c | 356 ++++++++++++++++--
>> drivers/mfd/axp20x-i2c.c | 2 +
>> drivers/mfd/axp20x.c | 141 +++++++
>> drivers/power/supply/axp20x_battery.c | 142 ++++++-
>> drivers/power/supply/axp20x_usb_power.c | 84 ++++-
>> drivers/regulator/axp20x-regulator.c | 100 ++++-
>> include/linux/mfd/axp20x.h | 84 +++++
>> 11 files changed, 856 insertions(+), 74 deletions(-)
>>
On Tue, Oct 18, 2022 at 2:06 AM Aidan MacDonald
<[email protected]> wrote:
> Jonathan Cameron <[email protected]> writes:
> > On Mon, 17 Oct 2022 00:43:22 +0100
> > Aidan MacDonald <[email protected]> wrote:
...
> > Lee has recently expressed that he keen to take as much of these sorts
> > of series as possible via the various subsystem trees.
> >
> > As such, it is useful to call out in the cover letter of such a series
> > if this can be done. For example, patch 9 (last IIO one) can't be
> > applied without defines in patch 6 (I think). Thus I'm assuming Lee
> > will do an immutable branch with at least those patches on it.
> >
> > Perhaps worth expressing if that is also the case for the power
> > and regulator subsystem patches?
> Yep, the IIO, regulator, and power subsystem patches all depend on
> the MFD patch.
There are two types of dependencies: compile and functional.
> Specifically, patches 6, 9, and 10 depend on patch 5.
> I can't get rid of this dependency because the variant ID (AXP192_ID)
> has to be defined centrally in the MFD patch.
It's not clear which one you are talking about. If it's functional,
then each driver can be taken separately via each concerned subsystem.
> The axp20x_battery patches (last three of the whole series) don't
> depend on the variant ID or other defines, so they could be taken
> independently through the power subsystem.
>
> Even though the IIO cleanups (7 and 8) don't depend on anything else
> I imagine it'd cause problems to take those via IIO because patch 9
> depends on them.
>
> IOW: Lee probably needs to take patches 5-10.
--
With Best Regards,
Andy Shevchenko
Andy Shevchenko <[email protected]> writes:
> On Tue, Oct 18, 2022 at 2:06 AM Aidan MacDonald
> <[email protected]> wrote:
>> Jonathan Cameron <[email protected]> writes:
>> > On Mon, 17 Oct 2022 00:43:22 +0100
>> > Aidan MacDonald <[email protected]> wrote:
>
> ...
>
>> > Lee has recently expressed that he keen to take as much of these sorts
>> > of series as possible via the various subsystem trees.
>> >
>> > As such, it is useful to call out in the cover letter of such a series
>> > if this can be done. For example, patch 9 (last IIO one) can't be
>> > applied without defines in patch 6 (I think). Thus I'm assuming Lee
>> > will do an immutable branch with at least those patches on it.
>> >
>> > Perhaps worth expressing if that is also the case for the power
>> > and regulator subsystem patches?
>
>> Yep, the IIO, regulator, and power subsystem patches all depend on
>> the MFD patch.
>
> There are two types of dependencies: compile and functional.
>
>> Specifically, patches 6, 9, and 10 depend on patch 5.
>> I can't get rid of this dependency because the variant ID (AXP192_ID)
>> has to be defined centrally in the MFD patch.
>
> It's not clear which one you are talking about. If it's functional,
> then each driver can be taken separately via each concerned subsystem.
Johnathan was talking about compile dependencies -- the defines he's
mentioning -- so what I mean is compile dependencies. Patches 6/9/10
do not compile unless they are applied after patch 5, because of a new
enumerator AXP192_ID defined in patch 5.
Regards,
Aidan
Hi Aidan,
Am 17.10.22 um 01:43 schrieb Aidan MacDonald:
> This series adds support for the AXP192 PMIC to the AXP20x MFD driver
> framework, including support for regulators, ADCs, and AC/USB/battery
> power supplies.
>
> v6 is a resend of v5 from July -- the patches haven't changed at all
> but I've rebased them on the latest git master branch.
I'm not entirely sure if I've found the latest version of the patches - at
least b4 didn't find a newer. It looks a lot like only mfd and usb-power
patches have been applied for some reason. Are you planing to resend the
other ones?
Regards,
Alex
> Aidan MacDonald (13):
> dt-bindings: mfd: add bindings for AXP192 MFD device
> dt-bindings: iio: adc: axp209: Add AXP192 compatible
> dt-bindings: power: supply: axp20x: Add AXP192 compatible
> dt-bindings: power: axp20x-battery: Add AXP192 compatible
> mfd: axp20x: Add support for AXP192
> regulator: axp20x: Add support for AXP192
> iio: adc: axp20x_adc: Minor code cleanups
> iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field
> iio: adc: axp20x_adc: Add support for AXP192
> power: supply: axp20x_usb_power: Add support for AXP192
> power: axp20x_battery: Add constant charge current table
> power: axp20x_battery: Support battery status without fuel gauge
> power: axp20x_battery: Add support for AXP192
>
> .../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 +
> .../bindings/mfd/x-powers,axp152.yaml | 1 +
> .../x-powers,axp20x-battery-power-supply.yaml | 1 +
> .../x-powers,axp20x-usb-power-supply.yaml | 1 +
> drivers/iio/adc/axp20x_adc.c | 356 ++++++++++++++++--
> drivers/mfd/axp20x-i2c.c | 2 +
> drivers/mfd/axp20x.c | 141 +++++++
> drivers/power/supply/axp20x_battery.c | 142 ++++++-
> drivers/power/supply/axp20x_usb_power.c | 84 ++++-
> drivers/regulator/axp20x-regulator.c | 100 ++++-
> include/linux/mfd/axp20x.h | 84 +++++
> 11 files changed, 856 insertions(+), 74 deletions(-)
>
On Fri, 7 Jun 2024 17:12:51 +0200
Alex Bee <[email protected]> wrote:
> Hi Aidan,
>
> Am 17.10.22 um 01:43 schrieb Aidan MacDonald:
> > This series adds support for the AXP192 PMIC to the AXP20x MFD driver
> > framework, including support for regulators, ADCs, and AC/USB/battery
> > power supplies.
> >
> > v6 is a resend of v5 from July -- the patches haven't changed at all
> > but I've rebased them on the latest git master branch.
> I'm not entirely sure if I've found the latest version of the patches - at
> least b4 didn't find a newer. It looks a lot like only mfd and usb-power
> patches have been applied for some reason. Are you planing to resend the
> other ones?
This was delayed originally by a dependency on a header in mfd and that is
obviously resolved now. I think everyone was expecting a resend
of the series or for Lee to pick up the dependent ones and so we all
stopped tracking it.
Anyhow, I had the two IIO cleanups already, but I've now picked up
the 3rd patch (where the dependency was) as well.
Thanks,
Jonathan
>
> Regards,
> Alex
>
> > Aidan MacDonald (13):
> > dt-bindings: mfd: add bindings for AXP192 MFD device
> > dt-bindings: iio: adc: axp209: Add AXP192 compatible
> > dt-bindings: power: supply: axp20x: Add AXP192 compatible
> > dt-bindings: power: axp20x-battery: Add AXP192 compatible
> > mfd: axp20x: Add support for AXP192
> > regulator: axp20x: Add support for AXP192
> > iio: adc: axp20x_adc: Minor code cleanups
> > iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field
> > iio: adc: axp20x_adc: Add support for AXP192
> > power: supply: axp20x_usb_power: Add support for AXP192
> > power: axp20x_battery: Add constant charge current table
> > power: axp20x_battery: Support battery status without fuel gauge
> > power: axp20x_battery: Add support for AXP192
> >
> > .../bindings/iio/adc/x-powers,axp209-adc.yaml | 18 +
> > .../bindings/mfd/x-powers,axp152.yaml | 1 +
> > .../x-powers,axp20x-battery-power-supply.yaml | 1 +
> > .../x-powers,axp20x-usb-power-supply.yaml | 1 +
> > drivers/iio/adc/axp20x_adc.c | 356 ++++++++++++++++--
> > drivers/mfd/axp20x-i2c.c | 2 +
> > drivers/mfd/axp20x.c | 141 +++++++
> > drivers/power/supply/axp20x_battery.c | 142 ++++++-
> > drivers/power/supply/axp20x_usb_power.c | 84 ++++-
> > drivers/regulator/axp20x-regulator.c | 100 ++++-
> > include/linux/mfd/axp20x.h | 84 +++++
> > 11 files changed, 856 insertions(+), 74 deletions(-)
> >
On Mon, 17 Oct 2022 00:43:31 +0100
Aidan MacDonald <[email protected]> wrote:
> The AXP192 is identical to the AXP20x, except for the addition of
> two more GPIO ADC channels.
>
> Reviewed-by: Jonathan Cameron <[email protected]>
> Reviewed-by: Andy Shevchenko <[email protected]>
> Signed-off-by: Aidan MacDonald <[email protected]>
Applied to the togreg branch of iio.git and pushed out as testing to
let the build bots see if we missed anything.
Thanks
Jonathan
> ---
> drivers/iio/adc/axp20x_adc.c | 279 +++++++++++++++++++++++++++++++++++
> 1 file changed, 279 insertions(+)
>
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> index 75bda94dbce1..8d748b814f20 100644
> --- a/drivers/iio/adc/axp20x_adc.c
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -22,11 +22,19 @@
> #include <linux/iio/machine.h>
> #include <linux/mfd/axp20x.h>
>
> +#define AXP192_ADC_EN1_MASK GENMASK(7, 0)
> +#define AXP192_ADC_EN2_MASK (GENMASK(3, 0) | BIT(7))
> +
> #define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
> #define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
>
> #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
>
> +#define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0)
> +#define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1)
> +#define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2)
> +#define AXP192_GPIO30_IN_RANGE_GPIO3 BIT(3)
> +
> #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
>
> @@ -71,6 +79,25 @@ struct axp20x_adc_iio {
> const struct axp_data *data;
> };
>
> +enum axp192_adc_channel_v {
> + AXP192_ACIN_V = 0,
> + AXP192_VBUS_V,
> + AXP192_TS_IN,
> + AXP192_GPIO0_V,
> + AXP192_GPIO1_V,
> + AXP192_GPIO2_V,
> + AXP192_GPIO3_V,
> + AXP192_IPSOUT_V,
> + AXP192_BATT_V,
> +};
> +
> +enum axp192_adc_channel_i {
> + AXP192_ACIN_I = 0,
> + AXP192_VBUS_I,
> + AXP192_BATT_CHRG_I,
> + AXP192_BATT_DISCHRG_I,
> +};
> +
> enum axp20x_adc_channel_v {
> AXP20X_ACIN_V = 0,
> AXP20X_VBUS_V,
> @@ -158,6 +185,43 @@ static struct iio_map axp22x_maps[] = {
> * The only exception is for the battery. batt_v will be in_voltage6_raw and
> * charge current in_current6_raw and discharge current will be in_current7_raw.
> */
> +static const struct iio_chan_spec axp192_adc_channels[] = {
> + AXP20X_ADC_CHANNEL(AXP192_ACIN_V, "acin_v", IIO_VOLTAGE,
> + AXP20X_ACIN_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP192_ACIN_I, "acin_i", IIO_CURRENT,
> + AXP20X_ACIN_I_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP192_VBUS_V, "vbus_v", IIO_VOLTAGE,
> + AXP20X_VBUS_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP192_VBUS_I, "vbus_i", IIO_CURRENT,
> + AXP20X_VBUS_I_ADC_H),
> + {
> + .type = IIO_TEMP,
> + .address = AXP20X_TEMP_ADC_H,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE) |
> + BIT(IIO_CHAN_INFO_OFFSET),
> + .datasheet_name = "pmic_temp",
> + },
> + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
> + AXP20X_GPIO0_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
> + AXP20X_GPIO1_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO2_V, "gpio2_v", IIO_VOLTAGE,
> + AXP192_GPIO2_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO3_V, "gpio3_v", IIO_VOLTAGE,
> + AXP192_GPIO3_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP192_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
> + AXP20X_IPSOUT_V_HIGH_H),
> + AXP20X_ADC_CHANNEL(AXP192_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP192_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP192_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP192_TS_IN, "ts_v", IIO_VOLTAGE,
> + AXP20X_TS_IN_H),
> +};
> +
> static const struct iio_chan_spec axp20x_adc_channels[] = {
> AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
> AXP20X_ACIN_V_ADC_H),
> @@ -231,6 +295,27 @@ static const struct iio_chan_spec axp813_adc_channels[] = {
> AXP288_TS_ADC_H),
> };
>
> +static int axp192_adc_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int ret, size;
> +
> + if (chan->type == IIO_CURRENT &&
> + (chan->channel == AXP192_BATT_CHRG_I ||
> + chan->channel == AXP192_BATT_DISCHRG_I))
> + size = 13;
> + else
> + size = 12;
> +
> + ret = axp20x_read_variable_width(info->regmap, chan->address, size);
> + if (ret < 0)
> + return ret;
> +
> + *val = ret;
> + return IIO_VAL_INT;
> +}
> +
> static int axp20x_adc_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val)
> {
> @@ -283,6 +368,44 @@ static int axp813_adc_raw(struct iio_dev *indio_dev,
> return IIO_VAL_INT;
> }
>
> +static int axp192_adc_scale_voltage(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP192_ACIN_V:
> + case AXP192_VBUS_V:
> + *val = 1;
> + *val2 = 700000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP192_GPIO0_V:
> + case AXP192_GPIO1_V:
> + case AXP192_GPIO2_V:
> + case AXP192_GPIO3_V:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP192_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP192_IPSOUT_V:
> + *val = 1;
> + *val2 = 400000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP192_TS_IN:
> + /* 0.8 mV per LSB */
> + *val = 0;
> + *val2 = 800000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
> {
> switch (channel) {
> @@ -386,6 +509,29 @@ static int axp20x_adc_scale_current(int channel, int *val, int *val2)
> }
> }
>
> +static int axp192_adc_scale(struct iio_chan_spec const *chan, int *val,
> + int *val2)
> +{
> + switch (chan->type) {
> + case IIO_VOLTAGE:
> + return axp192_adc_scale_voltage(chan->channel, val, val2);
> +
> + case IIO_CURRENT:
> + /*
> + * AXP192 current channels are identical to the AXP20x,
> + * therefore we can re-use the scaling function.
> + */
> + return axp20x_adc_scale_current(chan->channel, val, val2);
> +
> + case IIO_TEMP:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
> int *val2)
> {
> @@ -445,6 +591,42 @@ static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
> }
> }
>
> +static int axp192_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
> + int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + unsigned int regval;
> + int ret;
> +
> + ret = regmap_read(info->regmap, AXP192_GPIO30_IN_RANGE, ®val);
> + if (ret < 0)
> + return ret;
> +
> + switch (channel) {
> + case AXP192_GPIO0_V:
> + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO0, regval);
> + break;
> +
> + case AXP192_GPIO1_V:
> + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO1, regval);
> + break;
> +
> + case AXP192_GPIO2_V:
> + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO2, regval);
> + break;
> +
> + case AXP192_GPIO3_V:
> + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO3, regval);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + *val = regval ? 700000 : 0;
> + return IIO_VAL_INT;
> +}
> +
> static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
> int *val)
> {
> @@ -473,6 +655,22 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
> return IIO_VAL_INT;
> }
>
> +static int axp192_adc_offset(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val)
> +{
> + switch (chan->type) {
> + case IIO_VOLTAGE:
> + return axp192_adc_offset_voltage(indio_dev, chan->channel, val);
> +
> + case IIO_TEMP:
> + *val = -1447;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp20x_adc_offset(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val)
> {
> @@ -489,6 +687,25 @@ static int axp20x_adc_offset(struct iio_dev *indio_dev,
> }
> }
>
> +static int axp192_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_OFFSET:
> + return axp192_adc_offset(indio_dev, chan, val);
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp192_adc_scale(chan, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp192_adc_raw(indio_dev, chan, val);
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp20x_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val,
> int *val2, long mask)
> @@ -549,6 +766,51 @@ static int axp813_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> +static int axp192_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long mask)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + unsigned int regmask, regval;
> +
> + /*
> + * The AXP192 PMIC allows the user to choose between 0V and 0.7V offsets
> + * for (independently) GPIO0-3 when in ADC mode.
> + */
> + if (mask != IIO_CHAN_INFO_OFFSET)
> + return -EINVAL;
> +
> + if (val != 0 && val != 700000)
> + return -EINVAL;
> +
> + switch (chan->channel) {
> + case AXP192_GPIO0_V:
> + regmask = AXP192_GPIO30_IN_RANGE_GPIO0;
> + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO0, !!val);
> + break;
> +
> + case AXP192_GPIO1_V:
> + regmask = AXP192_GPIO30_IN_RANGE_GPIO1;
> + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO1, !!val);
> + break;
> +
> + case AXP192_GPIO2_V:
> + regmask = AXP192_GPIO30_IN_RANGE_GPIO2;
> + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO2, !!val);
> + break;
> +
> + case AXP192_GPIO3_V:
> + regmask = AXP192_GPIO30_IN_RANGE_GPIO3;
> + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO3, !!val);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return regmap_update_bits(info->regmap, AXP192_GPIO30_IN_RANGE, regmask, regval);
> +}
> +
> static int axp20x_write_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int val, int val2,
> long mask)
> @@ -584,6 +846,11 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
> return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval);
> }
>
> +static const struct iio_info axp192_adc_iio_info = {
> + .read_raw = axp192_read_raw,
> + .write_raw = axp192_write_raw,
> +};
> +
> static const struct iio_info axp20x_adc_iio_info = {
> .read_raw = axp20x_read_raw,
> .write_raw = axp20x_write_raw,
> @@ -629,6 +896,16 @@ struct axp_data {
> struct iio_map *maps;
> };
>
> +static const struct axp_data axp192_data = {
> + .iio_info = &axp192_adc_iio_info,
> + .num_channels = ARRAY_SIZE(axp192_adc_channels),
> + .channels = axp192_adc_channels,
> + .adc_en1_mask = AXP192_ADC_EN1_MASK,
> + .adc_en2_mask = AXP192_ADC_EN2_MASK,
> + .adc_rate = axp20x_adc_rate,
> + .maps = axp20x_maps,
> +};
> +
> static const struct axp_data axp20x_data = {
> .iio_info = &axp20x_adc_iio_info,
> .num_channels = ARRAY_SIZE(axp20x_adc_channels),
> @@ -658,6 +935,7 @@ static const struct axp_data axp813_data = {
> };
>
> static const struct of_device_id axp20x_adc_of_match[] = {
> + { .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, },
> { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
> { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
> { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
> @@ -666,6 +944,7 @@ static const struct of_device_id axp20x_adc_of_match[] = {
> MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
>
> static const struct platform_device_id axp20x_adc_id_match[] = {
> + { .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, },
> { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
> { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
> { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },