Re: [PATCH RESEND v6 4/5] mfd: axp20x: Add support for AXP318W PMIC

Chen-Yu Tsai <[email protected]>
Newsgroups gmane.linux.drivers.devicetree,gmane.linux.kernel
Message-ID <CAGb2v66Uz6hCQaf5Nk5Eup8wQ+Xo9tuAqm0+ZH9J5CbT0zyAPg@mail.gmail.com>
On Thu, Aug 13, 2026 at 5:31 AM Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/[email protected]> wrote:
>
> From: Andre Przywara <[email protected]>
>
> The AXP318W is a PMIC chip produced by X-Powers, it can be connected to
> an I2C bus.
>
> It has a large number of regulators: 9(!) DCDC buck converters, and 28
> LDOs, also some ADCs, interrupts, and a power key.
>
> Describe the regmap and the MFD bits, along with the registers exposed
> via I2C only. This covers the regulators and interrupts for now.
> Advertise the device using the new compatible string.
>
> We use just "318" for the internal identifiers, for easier typing and
> less churn, but use "318W" for anything externally visible. If something
> else other than the "AXP318W" shows up, that's an easy change then.
>
> Signed-off-by: Andre Przywara <[email protected]>
> Signed-off-by: Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/[email protected]>
> ---
>  drivers/mfd/axp20x-i2c.c   |  2 ++
>  drivers/mfd/axp20x.c       | 81 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/axp20x.h | 88 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 171 insertions(+)

[...]

> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index 84839d4aa1ca..ea00c0cdce58 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -218,6 +219,31 @@ static const struct regmap_access_table axp313a_volatile_table = {
>         .n_yes_ranges = ARRAY_SIZE(axp313a_volatile_ranges),
>  };
>
> +static const struct regmap_range axp318_writeable_ranges[] = {

  +       regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_DATACACHE(3)),

> +       regmap_reg_range(AXP318_DCDC_OUTPUT_CONTROL1, AXP318_IRQ_STATE4),

Probably want to skip the holes between:

- AXP318_DCDC9_CONTROL and AXP318_DCDC_MODE_CTRL1
- AXP318_DCDC_MODE_CTRL1 and AXP318_DCDC_MODE_CTRL3
- AXP318_DCDC_MODE_CTRL3 and AXP318_LDO_OUTPUT_CONTROL1
- AXP318_IRQ_EN4 and AXP318_IRQ_STATE1

> +       regmap_reg_range(AXP318_SHUTDOWN_SRC_CTRL1, AXP318_TEMP_ADC_H_EN),

Holes between

- AXP318_POK_CTRL and AXP318_TEMP_ADC_H_EN
- AXP318_WAKEUP_PWROK_PIN_CFG and AXP318_TS_PIN_CFG
- AXP318_TS_PIN_CFG and AXP318_TEMP_ADC_H_EN

> +       regmap_reg_range(AXP318_DIE_TEMP_ADC_H_EN, AXP318_DIE_TEMP_ADC_H_EN),
> +       regmap_reg_range(AXP318_GPADC_H_EN, AXP318_GPADC_H_EN),
> +       regmap_reg_range(AXP318_GPIO_CTRL, AXP318_WDOG_CTRL),
> +};
> +
> +static const struct regmap_range axp318_volatile_ranges[] = {
> +       regmap_reg_range(AXP318_IRQ_EN1, AXP318_IRQ_STATE4),

The EN bits aren't volatile; only the status bits are.

> +       regmap_reg_range(AXP318_POWER_REASON, AXP318_SHUTDOWN_SOURCE),
> +       regmap_reg_range(AXP318_TEMP_ADC_H_EN, AXP318_GPADC_L),
> +       regmap_reg_range(AXP318_GPIO_INPUT, AXP318_GPIO_INPUT),
> +};
> +
> +static const struct regmap_access_table axp318_writeable_table = {
> +       .yes_ranges = axp318_writeable_ranges,
> +       .n_yes_ranges = ARRAY_SIZE(axp318_writeable_ranges),
> +};
> +
> +static const struct regmap_access_table axp318_volatile_table = {
> +       .yes_ranges = axp318_volatile_ranges,
> +       .n_yes_ranges = ARRAY_SIZE(axp318_volatile_ranges),
> +};
> +
>  static const struct regmap_range axp717_writeable_ranges[] = {
>         regmap_reg_range(AXP717_PMU_FAULT, AXP717_MODULE_EN_CONTROL_1),
>         regmap_reg_range(AXP717_MIN_SYS_V_CONTROL, AXP717_BOOST_CONTROL),
> @@ -447,6 +473,15 @@ static const struct regmap_config axp313a_regmap_config = {
>         .cache_type = REGCACHE_MAPLE,
>  };
>
> +static const struct regmap_config axp318_regmap_config = {
> +       .reg_bits = 8,
> +       .val_bits = 8,
> +       .wr_table = &axp318_writeable_table,
> +       .volatile_table = &axp318_volatile_table,
> +       .max_register = AXP318_WDOG_CTRL,
> +       .cache_type = REGCACHE_MAPLE,
> +};
> +
>  static const struct regmap_config axp323_regmap_config = {
>         .reg_bits = 8,
>         .val_bits = 8,
> @@ -663,6 +698,28 @@ static const struct regmap_irq axp313a_regmap_irqs[] = {
>         INIT_REGMAP_IRQ(AXP313A, DIE_TEMP_HIGH,         0, 0),
>  };
>
> +static const struct regmap_irq axp318_regmap_irqs[] = {
> +       INIT_REGMAP_IRQ(AXP318, DCDC8_V_LOW,            0, 7),
> +       INIT_REGMAP_IRQ(AXP318, DCDC7_V_LOW,            0, 6),
> +       INIT_REGMAP_IRQ(AXP318, DCDC6_V_LOW,            0, 5),
> +       INIT_REGMAP_IRQ(AXP318, DCDC5_V_LOW,            0, 4),
> +       INIT_REGMAP_IRQ(AXP318, DCDC4_V_LOW,            0, 3),
> +       INIT_REGMAP_IRQ(AXP318, DCDC3_V_LOW,            0, 2),
> +       INIT_REGMAP_IRQ(AXP318, DCDC2_V_LOW,            0, 1),
> +       INIT_REGMAP_IRQ(AXP318, DCDC1_V_LOW,            0, 0),
> +       INIT_REGMAP_IRQ(AXP318, PEK_RIS_EDGE,           1, 6),
> +       INIT_REGMAP_IRQ(AXP318, PEK_FAL_EDGE,           1, 5),

> +       INIT_REGMAP_IRQ(AXP318, PEK_LONG,               1, 4),
> +       INIT_REGMAP_IRQ(AXP318, PEK_SHORT,              1, 3),

These two seem to be reversed.

> +       INIT_REGMAP_IRQ(AXP318, DIE_TEMP_HIGH_LV2,      1, 2),
> +       INIT_REGMAP_IRQ(AXP318, DIE_TEMP_HIGH_LV1,      1, 1),
> +       INIT_REGMAP_IRQ(AXP318, DCDC9_V_LOW,            1, 0),
> +       INIT_REGMAP_IRQ(AXP318, GPIO3_INPUT,            2, 6),
> +       INIT_REGMAP_IRQ(AXP318, GPIO2_INPUT,            2, 5),
> +       INIT_REGMAP_IRQ(AXP318, GPIO1_INPUT,            2, 4),
> +       INIT_REGMAP_IRQ(AXP318, WDOG_EXPIRE,            3, 0),
> +};
> +
>  static const struct regmap_irq axp717_regmap_irqs[] = {
>         INIT_REGMAP_IRQ(AXP717, SOC_DROP_LVL2,          0, 7),
>         INIT_REGMAP_IRQ(AXP717, SOC_DROP_LVL1,          0, 6),
> @@ -884,6 +941,17 @@ static const struct regmap_irq_chip axp313a_regmap_irq_chip = {
>         .num_regs               = 1,
>  };
>
> +static const struct regmap_irq_chip axp318_regmap_irq_chip = {
> +       .name                   = "axp318w_irq_chip",
> +       .status_base            = AXP318_IRQ_STATE1,
> +       .ack_base               = AXP318_IRQ_STATE1,
> +       .unmask_base            = AXP318_IRQ_EN1,
> +       .init_ack_masked        = true,
> +       .irqs                   = axp318_regmap_irqs,
> +       .num_irqs               = ARRAY_SIZE(axp318_regmap_irqs),
> +       .num_regs               = 4,
> +};
> +
>  static const struct regmap_irq_chip axp717_regmap_irq_chip = {
>         .name                   = "axp717_irq_chip",
>         .status_base            = AXP717_IRQ0_STATE,
> @@ -1061,6 +1129,10 @@ static const struct mfd_cell axp313a_cells[] = {
>         MFD_CELL_RES("axp313a-pek", axp313a_pek_resources),
>  };
>
> +static const struct mfd_cell axp318_cells[] = {
> +       MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),

Unless this PMIC is used as a sub-PMIC in other designs, we don't need an ID.
Allwinner designs "mostly" only have one PMIC. We add IDs only when needed,
as seen on the AXP313a and AXP806 + AXP809.

> +};
> +
>  static const struct mfd_cell axp717_cells[] = {
>         MFD_CELL_NAME("axp20x-regulator"),
>         MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
> @@ -1248,6 +1320,9 @@ static int axp20x_power_off(struct sys_off_data *data)
>         case AXP313A_ID:
>                 shutdown_reg = AXP313A_SHUTDOWN_CTRL;
>                 break;
> +       case AXP318_ID:
> +               shutdown_reg = AXP318_SHUTDOWN_CTRL;
> +               break;
>         default:
>                 shutdown_reg = AXP20X_OFF_CTRL;
>                 break;
> @@ -1312,6 +1387,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
>                 axp20x->regmap_cfg = &axp313a_regmap_config;
>                 axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip;
>                 break;
> +       case AXP318_ID:
> +               axp20x->nr_cells = ARRAY_SIZE(axp318_cells);
> +               axp20x->cells = axp318_cells;
> +               axp20x->regmap_cfg = &axp318_regmap_config;
> +               axp20x->regmap_irq_chip = &axp318_regmap_irq_chip;
> +               break;
>         case AXP323_ID:
>                 axp20x->nr_cells = ARRAY_SIZE(axp313a_cells);
>                 axp20x->cells = axp313a_cells;
> diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> index 1badb4868d17..0191467de4ec 100644
> --- a/include/linux/mfd/axp20x.h
> +++ b/include/linux/mfd/axp20x.h
> @@ -19,6 +19,7 @@ enum axp20x_variants {
>         AXP223_ID,
>         AXP288_ID,
>         AXP313A_ID,
> +       AXP318_ID,
>         AXP323_ID,
>         AXP717_ID,
>         AXP803_ID,
> @@ -116,6 +117,71 @@ enum axp20x_variants {
>  #define AXP313A_IRQ_STATE              0x21
>  #define AXP323_DCDC_MODE_CTRL2         0x22
>
> +#define AXP318_DCDC_OUTPUT_CONTROL1    0x10
> +#define AXP318_DCDC_OUTPUT_CONTROL2    0x11
> +#define AXP318_DCDC1_CONTROL           0x12
> +#define AXP318_DCDC2_CONTROL           0x13
> +#define AXP318_DCDC3_CONTROL           0x14
> +#define AXP318_DCDC4_CONTROL           0x15
> +#define AXP318_DCDC5_CONTROL           0x16
> +#define AXP318_DCDC6_CONTROL           0x17
> +#define AXP318_DCDC7_CONTROL           0x18
> +#define AXP318_DCDC8_CONTROL           0x19
> +#define AXP318_DCDC9_CONTROL           0x1a

+#define AXP318_DCDC_MODE_CTRL1           0x1b
+#define AXP318_DCDC_MODE_CTRL3           0x1d

> +#define AXP318_LDO_OUTPUT_CONTROL1     0x20
> +#define AXP318_LDO_OUTPUT_CONTROL2     0x21
> +#define AXP318_LDO_OUTPUT_CONTROL3     0x22
> +#define AXP318_LDO_OUTPUT_CONTROL4     0x23
> +#define AXP318_ALDO1_CONTROL           0x24
> +#define AXP318_ALDO2_CONTROL           0x25
> +#define AXP318_ALDO3_CONTROL           0x26
> +#define AXP318_ALDO4_CONTROL           0x27
> +#define AXP318_ALDO5_CONTROL           0x28
> +#define AXP318_ALDO6_CONTROL           0x29
> +#define AXP318_BLDO1_CONTROL           0x2a
> +#define AXP318_BLDO2_CONTROL           0x2b
> +#define AXP318_BLDO3_CONTROL           0x2c
> +#define AXP318_BLDO4_CONTROL           0x2d
> +#define AXP318_BLDO5_CONTROL           0x2e
> +#define AXP318_CLDO1_CONTROL           0x2f
> +#define AXP318_CLDO2_CONTROL           0x30
> +#define AXP318_CLDO3_CONTROL           0x31
> +#define AXP318_CLDO4_CONTROL           0x32
> +#define AXP318_CLDO5_CONTROL           0x33
> +#define AXP318_DLDO1_CONTROL           0x34
> +#define AXP318_DLDO2_CONTROL           0x35
> +#define AXP318_DLDO3_CONTROL           0x36
> +#define AXP318_DLDO4_CONTROL           0x37
> +#define AXP318_DLDO5_CONTROL           0x38
> +#define AXP318_DLDO6_CONTROL           0x39
> +#define AXP318_ELDO1_CONTROL           0x3a
> +#define AXP318_ELDO2_CONTROL           0x3b
> +#define AXP318_ELDO3_CONTROL           0x3c
> +#define AXP318_ELDO4_CONTROL           0x3d
> +#define AXP318_ELDO5_CONTROL           0x3e
> +#define AXP318_ELDO6_CONTROL           0x3f
> +#define AXP318_IRQ_EN1                 0x40
> +#define AXP318_IRQ_EN2                 0x41
> +#define AXP318_IRQ_EN3                 0x42
> +#define AXP318_IRQ_EN4                 0x43
> +#define AXP318_IRQ_STATE1              0x48
> +#define AXP318_IRQ_STATE2              0x49
> +#define AXP318_IRQ_STATE3              0x4a
> +#define AXP318_IRQ_STATE4              0x4b
> +#define AXP318_POWER_REASON            0x50
> +#define AXP318_SHUTDOWN_SOURCE         0x51
> +#define AXP318_SHUTDOWN_SRC_CTRL1      0x52
> +#define AXP318_SHUTDOWN_SRC_CTRL2      0x53

  +#define AXP318_DISCHARGE_OVERTEMP_CTRL 0x54

> +#define AXP318_SHUTDOWN_CTRL           0x55

  +#define AXP318_WAKEUP_VOFF_CTRL        0x56
  +#define AXP318_POK_CTRL                0x57

  +#define AXP318_WAKEUP_PWROK_PIN_CFG    0x5d

  +#define AXP318_TS_PIN_CFG              0x60

> +#define AXP318_TEMP_ADC_H_EN           0x65
> +#define AXP318_TEMP_ADC_L              0x66
> +#define AXP318_DIE_TEMP_ADC_H_EN       0x67
  +#define AXP318_DIE_TEMP_ADC_L          0x68
> +#define AXP318_GPADC_H_EN              0x69
> +#define AXP318_GPADC_L                 0x6a
> +#define AXP318_GPIO_CTRL               0x70
> +#define AXP318_GPIO_INPUT              0x71

  +#define AXP318_GPIO_OUTPUT             0x72
  +#define AXP318_PWM_CTRL1               0x73
  +#define AXP318_PWM_CTRL2               0x74
  +#define AXP318_PWM_CTRL3               0x75
  +#define AXP318_BACKUP_BATTERY_CTRL     0x76

> +#define AXP318_WDOG_CTRL               0x77
> +
>  #define AXP717_ON_INDICATE             0x00
>  #define AXP717_PMU_STATUS_2            0x01
>  #define AXP717_BC_DETECT               0x05
> @@ -819,6 +885,28 @@ enum axp313a_irqs {
>         AXP313A_IRQ_PEK_RIS_EDGE,
>  };
>
> +enum axp318_irqs {
> +       AXP318_IRQ_DCDC1_V_LOW,
> +       AXP318_IRQ_DCDC2_V_LOW,
> +       AXP318_IRQ_DCDC3_V_LOW,
> +       AXP318_IRQ_DCDC4_V_LOW,
> +       AXP318_IRQ_DCDC5_V_LOW,
> +       AXP318_IRQ_DCDC6_V_LOW,
> +       AXP318_IRQ_DCDC7_V_LOW,
> +       AXP318_IRQ_DCDC8_V_LOW,
> +       AXP318_IRQ_DCDC9_V_LOW,
> +       AXP318_IRQ_DIE_TEMP_HIGH_LV1,
> +       AXP318_IRQ_DIE_TEMP_HIGH_LV2,

> +       AXP318_IRQ_PEK_SHORT,
> +       AXP318_IRQ_PEK_LONG,

The order of these two are reversed.

> +       AXP318_IRQ_PEK_FAL_EDGE,
> +       AXP318_IRQ_PEK_RIS_EDGE,

And these two as well.

Once all these are fixed,
Reviewed-by: Chen-Yu Tsai <[email protected]>


ChenYu

> +       AXP318_IRQ_GPIO1_INPUT = 20,
> +       AXP318_IRQ_GPIO2_INPUT,
> +       AXP318_IRQ_GPIO3_INPUT,
> +       AXP318_IRQ_WDOG_EXPIRE = 24,
> +};
> +
>  enum axp717_irqs {
>         AXP717_IRQ_VBUS_FAULT,
>         AXP717_IRQ_VBUS_OVER_V,
>
> --
> 2.47.3
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.