[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAGb2v64H9_7sPuDDkOKhNQu3uV0NMYzDm2UVFtdKDpp=Dcu=TA@mail.gmail.com>
Date: Mon, 3 Nov 2025 23:49:37 +0800
From: Chen-Yu Tsai <wens@...nel.org>
To: Andre Przywara <andre.przywara@....com>
Cc: Lee Jones <lee@...nel.org>, Liam Girdwood <lgirdwood@...il.com>, Mark Brown <broonie@...nel.org>,
Samuel Holland <samuel@...lland.org>, Jernej Skrabec <jernej.skrabec@...il.com>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>,
Yixun Lan <dlan@...too.org>, devicetree@...r.kernel.org, linux-sunxi@...ts.linux.dev,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/3] mfd: axp20x: Add support for AXP318W PMIC
On Tue, Oct 21, 2025 at 7:20 PM Andre Przywara <andre.przywara@....com> wrote:
>
> 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 regulator, interrupts and power key
> devices 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 <andre.przywara@....com>
> ---
> drivers/mfd/axp20x-i2c.c | 2 +
> drivers/mfd/axp20x.c | 84 +++++++++++++++++++++++++++++++++++++
> include/linux/mfd/axp20x.h | 86 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 172 insertions(+)
>
> diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c
> index 5c93136f977e7..4e4ebfc78525c 100644
> --- a/drivers/mfd/axp20x-i2c.c
> +++ b/drivers/mfd/axp20x-i2c.c
> @@ -65,6 +65,7 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
> { .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
> { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
> { .compatible = "x-powers,axp313a", .data = (void *)AXP313A_ID },
> + { .compatible = "x-powers,axp318w", .data = (void *)AXP318_ID },
> { .compatible = "x-powers,axp323", .data = (void *)AXP323_ID },
> { .compatible = "x-powers,axp717", .data = (void *)AXP717_ID },
> { .compatible = "x-powers,axp803", .data = (void *)AXP803_ID },
> @@ -83,6 +84,7 @@ static const struct i2c_device_id axp20x_i2c_id[] = {
> { "axp221" },
> { "axp223" },
> { "axp313a" },
> + { "axp318w" },
> { "axp717" },
> { "axp803" },
> { "axp806" },
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index c5f0ebae327f5..be9c59e3de071 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -42,6 +42,7 @@ static const char * const axp20x_model_names[] = {
> [AXP223_ID] = "AXP223",
> [AXP288_ID] = "AXP288",
> [AXP313A_ID] = "AXP313a",
> + [AXP318_ID] = "AXP318W",
> [AXP323_ID] = "AXP323",
> [AXP717_ID] = "AXP717",
> [AXP803_ID] = "AXP803",
> @@ -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(AXP318_DCDC_OUTPUT_CONTROL1, AXP318_IRQ_STATE4),
> + regmap_reg_range(AXP318_SHUTDOWN_CTRL, 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),
> + regmap_reg_range(AXP318_POWER_REASON, AXP318_SHUTDOWN_REASON),
> + 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),
> +};
I'd probably add a .no_ranges for both readable and writeable tables
for the various holes in the register range:
- 0x00 ~ 0x03
- 0x08 ~ 0x0f
- 0x1c
- 0x44 ~ 0x47
- 0x4c ~ 0x4f
- 0x58 ~ 0x5c
- 0x5e ~ 0x5f
- 0x61 ~ 0x64
- 0x6b ~ 0x6f
> +
> +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),
> @@ -365,6 +391,11 @@ static const struct resource axp313a_pek_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP313A_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
> };
>
> +static const struct resource axp318_pek_resources[] = {
> + DEFINE_RES_IRQ_NAMED(AXP318_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
> + DEFINE_RES_IRQ_NAMED(AXP318_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
> +};
> +
> static const struct resource axp717_pek_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP717_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
> DEFINE_RES_IRQ_NAMED(AXP717_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
> @@ -444,6 +475,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,
> @@ -660,6 +700,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),
The datasheet I have (0.1 draft in Chinese) says bit 3 is long press
and bit 4 is short press.
> + 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),
> @@ -881,6 +943,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,
> @@ -1058,6 +1131,11 @@ static struct mfd_cell axp313a_cells[] = {
> MFD_CELL_RES("axp313a-pek", axp313a_pek_resources),
> };
>
> +static struct mfd_cell axp318_cells[] = {
> + MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),
> + MFD_CELL_RES("axp318w-pek", axp318_pek_resources),
> +};
> +
> static struct mfd_cell axp717_cells[] = {
> MFD_CELL_NAME("axp20x-regulator"),
> MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
> @@ -1310,6 +1388,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 3c5aecf1d4b5b..a871789f6cfa9 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,69 @@ enum axp20x_variants {
> #define AXP313A_IRQ_STATE 0x21
> #define AXP323_DCDC_MODE_CTRL2 0x22
>
registers 0x04 ~ 0x07 are data registers. Shall we use them for NVMEM?
> +#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
0x1b and 0x1d control DCDC modes. Or are you leaving out registers that
aren't used?
> +#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_REASON 0x51
> +#define AXP318_SHUTDOWN_CTRL 0x52
> +#define AXP318_TEMP_ADC_H_EN 0x65
> +#define AXP318_TEMP_ADC_L 0x66
> +#define AXP318_DIE_TEMP_ADC_H_EN 0x67
> +#define AXP318_GPADC_H_EN 0x69
> +#define AXP318_GPADC_L 0x6a
> +#define AXP318_GPIO_CTRL 0x70
> +#define AXP318_GPIO_INPUT 0x71
> +#define AXP318_WDOG_CTRL 0x77
> +
> #define AXP717_ON_INDICATE 0x00
> #define AXP717_PMU_STATUS_2 0x01
> #define AXP717_BC_DETECT 0x05
> @@ -816,6 +880,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 datasheet I have (0.1 draft in Chinese) says long press comes before
short press.
> + AXP318_IRQ_PEK_FAL_EDGE,
> + AXP318_IRQ_PEK_RIS_EDGE,
> + AXP318_IRQ_GPIO1_INPUT = 20,
AFAICT the interrupt numbers don't have to align with the bit offsets,
even though we've been doing it for past models.
ChenYu
> + 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.25.1
>
Powered by blists - more mailing lists