diff --git a/target/linux/generic/backport-6.1/850-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch b/target/linux/generic/backport-6.1/850-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch new file mode 100644 index 000000000..b742276c8 --- /dev/null +++ b/target/linux/generic/backport-6.1/850-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch @@ -0,0 +1,82 @@ +From 1b1305e95e85624f538ec56db9acf88e2d3d7397 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Wed, 28 Dec 2022 10:27:52 -0600 +Subject: [PATCH] mfd: axp20x: Switch to the sys-off handler API + +This removes a layer of indirection through pm_power_off() and allows +the PMIC handler to be used as a fallback when firmware power off fails. +This happens on boards like the Clockwork DevTerm R-01 where OpenSBI +does not know how to use the PMIC to power off the board. + +Move the check for AXP288 to avoid registering a dummy handler. + +Signed-off-by: Samuel Holland +[Lee: Removed superfluous new line] +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20221228162752.14204-1-samuel@sholland.org +--- + drivers/mfd/axp20x.c | 27 +++++++++++---------------- + 1 file changed, 11 insertions(+), 16 deletions(-) + +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -23,7 +23,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + +@@ -832,17 +832,16 @@ static const struct mfd_cell axp813_cell + }, + }; + +-static struct axp20x_dev *axp20x_pm_power_off; +-static void axp20x_power_off(void) ++static int axp20x_power_off(struct sys_off_data *data) + { +- if (axp20x_pm_power_off->variant == AXP288_ID) +- return; ++ struct axp20x_dev *axp20x = data->cb_data; + +- regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, +- AXP20X_OFF); ++ regmap_write(axp20x->regmap, AXP20X_OFF_CTRL, AXP20X_OFF); + + /* Give capacitors etc. time to drain to avoid kernel panic msg. */ + mdelay(500); ++ ++ return NOTIFY_DONE; + } + + int axp20x_match_device(struct axp20x_dev *axp20x) +@@ -1009,10 +1008,11 @@ int axp20x_device_probe(struct axp20x_de + return ret; + } + +- if (!pm_power_off) { +- axp20x_pm_power_off = axp20x; +- pm_power_off = axp20x_power_off; +- } ++ if (axp20x->variant != AXP288_ID) ++ devm_register_sys_off_handler(axp20x->dev, ++ SYS_OFF_MODE_POWER_OFF, ++ SYS_OFF_PRIO_DEFAULT, ++ axp20x_power_off, axp20x); + + dev_info(axp20x->dev, "AXP20X driver loaded\n"); + +@@ -1022,11 +1022,6 @@ EXPORT_SYMBOL(axp20x_device_probe); + + void axp20x_device_remove(struct axp20x_dev *axp20x) + { +- if (axp20x == axp20x_pm_power_off) { +- axp20x_pm_power_off = NULL; +- pm_power_off = NULL; +- } +- + mfd_remove_devices(axp20x->dev); + regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); + } diff --git a/target/linux/generic/backport-6.1/851-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch b/target/linux/generic/backport-6.1/851-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch new file mode 100644 index 000000000..b74283edd --- /dev/null +++ b/target/linux/generic/backport-6.1/851-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch @@ -0,0 +1,256 @@ +From 75c8cb2f4cb218aaf4ea68cab08d6dbc96eeae15 Mon Sep 17 00:00:00 2001 +From: Martin Botka +Date: Wed, 24 May 2023 01:00:10 +0100 +Subject: [PATCH] mfd: axp20x: Add support for AXP313a PMIC + +The AXP313a is a PMIC chip produced by X-Powers, it can be connected via +an I2C bus. +The name AXP1530 seems to appear as well, and this is what is used in +the BSP driver. From all we know it's the same chip, just a different +name. However we have only seen AXP313a chips in the wild, so go with +this name. + +Compared to the other AXP PMICs it's a rather simple affair: just three +DCDC converters, three LDOs, and no battery charging support. + +Describe the regmap and the MFD bits, along with the registers exposed +via I2C. Aside from the various regulators, also describe the power key +interrupts, and adjust the shutdown handler routine to use a different +register than the other PMICs. +Eventually advertise the device using the new compatible string. + +Signed-off-by: Martin Botka +Signed-off-by: Andre Przywara +Reviewed-by: Chen-Yu Tsai +Link: https://lore.kernel.org/r/20230524000012.15028-2-andre.przywara@arm.com +Signed-off-by: Lee Jones +--- + drivers/mfd/axp20x-i2c.c | 2 + + drivers/mfd/axp20x.c | 78 +++++++++++++++++++++++++++++++++++++- + include/linux/mfd/axp20x.h | 32 ++++++++++++++++ + 3 files changed, 111 insertions(+), 1 deletion(-) + +--- a/drivers/mfd/axp20x-i2c.c ++++ b/drivers/mfd/axp20x-i2c.c +@@ -64,6 +64,7 @@ static const struct of_device_id axp20x_ + { .compatible = "x-powers,axp209", .data = (void *)AXP209_ID }, + { .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,axp803", .data = (void *)AXP803_ID }, + { .compatible = "x-powers,axp806", .data = (void *)AXP806_ID }, + { }, +@@ -77,6 +78,7 @@ static const struct i2c_device_id axp20x + { "axp209", 0 }, + { "axp221", 0 }, + { "axp223", 0 }, ++ { "axp313a", 0 }, + { "axp803", 0 }, + { "axp806", 0 }, + { }, +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -39,6 +39,7 @@ static const char * const axp20x_model_n + "AXP221", + "AXP223", + "AXP288", ++ "AXP313a", + "AXP803", + "AXP806", + "AXP809", +@@ -154,6 +155,25 @@ static const struct regmap_range axp806_ + regmap_reg_range(AXP806_REG_ADDR_EXT, AXP806_REG_ADDR_EXT), + }; + ++static const struct regmap_range axp313a_writeable_ranges[] = { ++ regmap_reg_range(AXP313A_ON_INDICATE, AXP313A_IRQ_STATE), ++}; ++ ++static const struct regmap_range axp313a_volatile_ranges[] = { ++ regmap_reg_range(AXP313A_SHUTDOWN_CTRL, AXP313A_SHUTDOWN_CTRL), ++ regmap_reg_range(AXP313A_IRQ_STATE, AXP313A_IRQ_STATE), ++}; ++ ++static const struct regmap_access_table axp313a_writeable_table = { ++ .yes_ranges = axp313a_writeable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(axp313a_writeable_ranges), ++}; ++ ++static const struct regmap_access_table axp313a_volatile_table = { ++ .yes_ranges = axp313a_volatile_ranges, ++ .n_yes_ranges = ARRAY_SIZE(axp313a_volatile_ranges), ++}; ++ + static const struct regmap_range axp806_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), + }; +@@ -221,6 +241,11 @@ static const struct resource axp288_fuel + DEFINE_RES_IRQ(AXP288_IRQ_WL1), + }; + ++static const struct resource axp313a_pek_resources[] = { ++ DEFINE_RES_IRQ_NAMED(AXP313A_IRQ_PEK_RIS_EDGE, "PEK_DBR"), ++ DEFINE_RES_IRQ_NAMED(AXP313A_IRQ_PEK_FAL_EDGE, "PEK_DBF"), ++}; ++ + static const struct resource axp803_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_RIS_EDGE, "PEK_DBR"), + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_FAL_EDGE, "PEK_DBF"), +@@ -272,6 +297,15 @@ static const struct regmap_config axp288 + .cache_type = REGCACHE_RBTREE, + }; + ++static const struct regmap_config axp313a_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .wr_table = &axp313a_writeable_table, ++ .volatile_table = &axp313a_volatile_table, ++ .max_register = AXP313A_IRQ_STATE, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ + static const struct regmap_config axp806_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +@@ -415,6 +449,16 @@ static const struct regmap_irq axp288_re + INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), + }; + ++static const struct regmap_irq axp313a_regmap_irqs[] = { ++ INIT_REGMAP_IRQ(AXP313A, PEK_RIS_EDGE, 0, 7), ++ INIT_REGMAP_IRQ(AXP313A, PEK_FAL_EDGE, 0, 6), ++ INIT_REGMAP_IRQ(AXP313A, PEK_SHORT, 0, 5), ++ INIT_REGMAP_IRQ(AXP313A, PEK_LONG, 0, 4), ++ INIT_REGMAP_IRQ(AXP313A, DCDC3_V_LOW, 0, 3), ++ INIT_REGMAP_IRQ(AXP313A, DCDC2_V_LOW, 0, 2), ++ INIT_REGMAP_IRQ(AXP313A, DIE_TEMP_HIGH, 0, 0), ++}; ++ + static const struct regmap_irq axp803_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP803, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP803, ACIN_PLUGIN, 0, 6), +@@ -552,6 +596,17 @@ static const struct regmap_irq_chip axp2 + + }; + ++static const struct regmap_irq_chip axp313a_regmap_irq_chip = { ++ .name = "axp313a_irq_chip", ++ .status_base = AXP313A_IRQ_STATE, ++ .ack_base = AXP313A_IRQ_STATE, ++ .unmask_base = AXP313A_IRQ_EN, ++ .init_ack_masked = true, ++ .irqs = axp313a_regmap_irqs, ++ .num_irqs = ARRAY_SIZE(axp313a_regmap_irqs), ++ .num_regs = 1, ++}; ++ + static const struct regmap_irq_chip axp803_regmap_irq_chip = { + .name = "axp803", + .status_base = AXP20X_IRQ1_STATE, +@@ -683,6 +738,11 @@ static const struct mfd_cell axp152_cell + }, + }; + ++static struct mfd_cell axp313a_cells[] = { ++ MFD_CELL_NAME("axp20x-regulator"), ++ MFD_CELL_RES("axp313a-pek", axp313a_pek_resources), ++}; ++ + static const struct resource axp288_adc_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP288_IRQ_GPADC, "GPADC"), + }; +@@ -835,8 +895,18 @@ static const struct mfd_cell axp813_cell + static int axp20x_power_off(struct sys_off_data *data) + { + struct axp20x_dev *axp20x = data->cb_data; ++ unsigned int shutdown_reg; + +- regmap_write(axp20x->regmap, AXP20X_OFF_CTRL, AXP20X_OFF); ++ switch (axp20x->variant) { ++ case AXP313A_ID: ++ shutdown_reg = AXP313A_SHUTDOWN_CTRL; ++ break; ++ default: ++ shutdown_reg = AXP20X_OFF_CTRL; ++ break; ++ } ++ ++ regmap_write(axp20x->regmap, shutdown_reg, AXP20X_OFF); + + /* Give capacitors etc. time to drain to avoid kernel panic msg. */ + mdelay(500); +@@ -899,6 +969,12 @@ int axp20x_match_device(struct axp20x_de + axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; + axp20x->irq_flags = IRQF_TRIGGER_LOW; + break; ++ case AXP313A_ID: ++ axp20x->nr_cells = ARRAY_SIZE(axp313a_cells); ++ axp20x->cells = axp313a_cells; ++ axp20x->regmap_cfg = &axp313a_regmap_config; ++ axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip; ++ break; + case AXP803_ID: + axp20x->nr_cells = ARRAY_SIZE(axp803_cells); + axp20x->cells = axp803_cells; +--- a/include/linux/mfd/axp20x.h ++++ b/include/linux/mfd/axp20x.h +@@ -17,6 +17,7 @@ enum axp20x_variants { + AXP221_ID, + AXP223_ID, + AXP288_ID, ++ AXP313A_ID, + AXP803_ID, + AXP806_ID, + AXP809_ID, +@@ -91,6 +92,17 @@ enum axp20x_variants { + #define AXP22X_ALDO3_V_OUT 0x2a + #define AXP22X_CHRG_CTRL3 0x35 + ++#define AXP313A_ON_INDICATE 0x00 ++#define AXP313A_OUTPUT_CONTROL 0x10 ++#define AXP313A_DCDC1_CONRTOL 0x13 ++#define AXP313A_DCDC2_CONRTOL 0x14 ++#define AXP313A_DCDC3_CONRTOL 0x15 ++#define AXP313A_ALDO1_CONRTOL 0x16 ++#define AXP313A_DLDO1_CONRTOL 0x17 ++#define AXP313A_SHUTDOWN_CTRL 0x1a ++#define AXP313A_IRQ_EN 0x20 ++#define AXP313A_IRQ_STATE 0x21 ++ + #define AXP806_STARTUP_SRC 0x00 + #define AXP806_CHIP_ID 0x03 + #define AXP806_PWR_OUT_CTRL1 0x10 +@@ -323,6 +335,16 @@ enum { + }; + + enum { ++ AXP313A_DCDC1 = 0, ++ AXP313A_DCDC2, ++ AXP313A_DCDC3, ++ AXP313A_ALDO1, ++ AXP313A_DLDO1, ++ AXP313A_RTC_LDO, ++ AXP313A_REG_ID_MAX, ++}; ++ ++enum { + AXP806_DCDCA = 0, + AXP806_DCDCB, + AXP806_DCDCC, +@@ -545,6 +567,16 @@ enum axp288_irqs { + AXP288_IRQ_BC_USB_CHNG, + }; + ++enum axp313a_irqs { ++ AXP313A_IRQ_DIE_TEMP_HIGH, ++ AXP313A_IRQ_DCDC2_V_LOW = 2, ++ AXP313A_IRQ_DCDC3_V_LOW, ++ AXP313A_IRQ_PEK_LONG, ++ AXP313A_IRQ_PEK_SHORT, ++ AXP313A_IRQ_PEK_FAL_EDGE, ++ AXP313A_IRQ_PEK_RIS_EDGE, ++}; ++ + enum axp803_irqs { + AXP803_IRQ_ACIN_OVER_V = 1, + AXP803_IRQ_ACIN_PLUGIN, diff --git a/target/linux/generic/backport-6.1/852-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch b/target/linux/generic/backport-6.1/852-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch new file mode 100644 index 000000000..91f1b3982 --- /dev/null +++ b/target/linux/generic/backport-6.1/852-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch @@ -0,0 +1,129 @@ +From 60fd7eb89670d2636ac3156881acbd103c6eba6a Mon Sep 17 00:00:00 2001 +From: Martin Botka +Date: Wed, 24 May 2023 01:00:11 +0100 +Subject: [PATCH] regulator: axp20x: Add support for AXP313a variant + +The AXP313a is your typical I2C controlled PMIC, although in a lighter +fashion compared to the other X-Powers PMICs: it has only three DCDC +rails, three LDOs, and no battery charging support. + +The AXP313a datasheet does not describe a register to change the DCDC +switching frequency, and talks of it being fixed at 3 MHz. Check that +the property allowing to change that frequency is absent from the DT, +and bail out otherwise. + +The third LDO, RTCLDO, is fixed, and cannot even be turned on or off, +programmatically. On top of that, its voltage is customisable (either +1.8V or 3.3V), which we cannot describe easily using the existing +regulator wrapper functions. This should be fixed properly, using +regulator-{min,max}-microvolt in the DT, but this requires more changes +to the code. As some other PMICs (AXP2xx, AXP803) seem to paper over the +same problem as well, we follow suit here and pretend it's a fixed 1.8V +regulator. A proper fix can follow later. The BSP code seems to ignore +this regulator altogether. + +Describe the AXP313A's voltage settings and switch registers, how the +voltages are encoded, and connect this to the MFD device via its +regulator ID. + +Signed-off-by: Martin Botka +Signed-off-by: Andre Przywara +Reviewed-by: Chen-Yu Tsai +Reviewed-by: Mark Brown +Tested-by: Shengyu Qu +Link: https://lore.kernel.org/r/20230524000012.15028-3-andre.przywara@arm.com +Signed-off-by: Mark Brown +--- + drivers/regulator/axp20x-regulator.c | 60 ++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -134,6 +134,11 @@ + #define AXP22X_PWR_OUT_DLDO4_MASK BIT_MASK(6) + #define AXP22X_PWR_OUT_ALDO3_MASK BIT_MASK(7) + ++#define AXP313A_DCDC1_NUM_VOLTAGES 107 ++#define AXP313A_DCDC23_NUM_VOLTAGES 88 ++#define AXP313A_DCDC_V_OUT_MASK GENMASK(6, 0) ++#define AXP313A_LDO_V_OUT_MASK GENMASK(4, 0) ++ + #define AXP803_PWR_OUT_DCDC1_MASK BIT_MASK(0) + #define AXP803_PWR_OUT_DCDC2_MASK BIT_MASK(1) + #define AXP803_PWR_OUT_DCDC3_MASK BIT_MASK(2) +@@ -638,6 +643,48 @@ static const struct regulator_desc axp22 + .ops = &axp20x_ops_sw, + }; + ++static const struct linear_range axp313a_dcdc1_ranges[] = { ++ REGULATOR_LINEAR_RANGE(500000, 0, 70, 10000), ++ REGULATOR_LINEAR_RANGE(1220000, 71, 87, 20000), ++ REGULATOR_LINEAR_RANGE(1600000, 88, 106, 100000), ++}; ++ ++static const struct linear_range axp313a_dcdc2_ranges[] = { ++ REGULATOR_LINEAR_RANGE(500000, 0, 70, 10000), ++ REGULATOR_LINEAR_RANGE(1220000, 71, 87, 20000), ++}; ++ ++/* ++ * This is deviating from the datasheet. The values here are taken from the ++ * BSP driver and have been confirmed by measurements. ++ */ ++static const struct linear_range axp313a_dcdc3_ranges[] = { ++ REGULATOR_LINEAR_RANGE(500000, 0, 70, 10000), ++ REGULATOR_LINEAR_RANGE(1220000, 71, 102, 20000), ++}; ++ ++static const struct regulator_desc axp313a_regulators[] = { ++ AXP_DESC_RANGES(AXP313A, DCDC1, "dcdc1", "vin1", ++ axp313a_dcdc1_ranges, AXP313A_DCDC1_NUM_VOLTAGES, ++ AXP313A_DCDC1_CONRTOL, AXP313A_DCDC_V_OUT_MASK, ++ AXP313A_OUTPUT_CONTROL, BIT(0)), ++ AXP_DESC_RANGES(AXP313A, DCDC2, "dcdc2", "vin2", ++ axp313a_dcdc2_ranges, AXP313A_DCDC23_NUM_VOLTAGES, ++ AXP313A_DCDC2_CONRTOL, AXP313A_DCDC_V_OUT_MASK, ++ AXP313A_OUTPUT_CONTROL, BIT(1)), ++ AXP_DESC_RANGES(AXP313A, DCDC3, "dcdc3", "vin3", ++ axp313a_dcdc3_ranges, AXP313A_DCDC23_NUM_VOLTAGES, ++ AXP313A_DCDC3_CONRTOL, AXP313A_DCDC_V_OUT_MASK, ++ AXP313A_OUTPUT_CONTROL, BIT(2)), ++ AXP_DESC(AXP313A, ALDO1, "aldo1", "vin1", 500, 3500, 100, ++ AXP313A_ALDO1_CONRTOL, AXP313A_LDO_V_OUT_MASK, ++ AXP313A_OUTPUT_CONTROL, BIT(3)), ++ AXP_DESC(AXP313A, DLDO1, "dldo1", "vin1", 500, 3500, 100, ++ AXP313A_DLDO1_CONRTOL, AXP313A_LDO_V_OUT_MASK, ++ AXP313A_OUTPUT_CONTROL, BIT(4)), ++ AXP_DESC_FIXED(AXP313A, RTC_LDO, "rtc-ldo", "vin1", 1800), ++}; ++ + /* DCDC ranges shared with AXP813 */ + static const struct linear_range axp803_dcdc234_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, +@@ -1040,6 +1087,15 @@ static int axp20x_set_dcdc_freq(struct p + def = 3000; + step = 150; + break; ++ case AXP313A_ID: ++ /* The DCDC PWM frequency seems to be fixed to 3 MHz. */ ++ if (dcdcfreq != 0) { ++ dev_err(&pdev->dev, ++ "DCDC frequency on AXP313a is fixed to 3 MHz.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; + default: + dev_err(&pdev->dev, + "Setting DCDC frequency for unsupported AXP variant\n"); +@@ -1232,6 +1288,10 @@ static int axp20x_regulator_probe(struct + drivevbus = of_property_read_bool(pdev->dev.parent->of_node, + "x-powers,drive-vbus-en"); + break; ++ case AXP313A_ID: ++ regulators = axp313a_regulators; ++ nregulators = AXP313A_REG_ID_MAX; ++ break; + case AXP803_ID: + regulators = axp803_regulators; + nregulators = AXP803_REG_ID_MAX;