summaryrefslogtreecommitdiff
path: root/drivers/mfd/axp20x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/axp20x.c')
-rw-r--r--drivers/mfd/axp20x.c266
1 files changed, 196 insertions, 70 deletions
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index c03bc5cda080..c5f0ebae327f 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -22,7 +22,8 @@
#include <linux/mfd/axp20x.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -33,19 +34,21 @@
#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
static const char * const axp20x_model_names[] = {
- "AXP152",
- "AXP192",
- "AXP202",
- "AXP209",
- "AXP221",
- "AXP223",
- "AXP288",
- "AXP313a",
- "AXP803",
- "AXP806",
- "AXP809",
- "AXP813",
- "AXP15060",
+ [AXP152_ID] = "AXP152",
+ [AXP192_ID] = "AXP192",
+ [AXP202_ID] = "AXP202",
+ [AXP209_ID] = "AXP209",
+ [AXP221_ID] = "AXP221",
+ [AXP223_ID] = "AXP223",
+ [AXP288_ID] = "AXP288",
+ [AXP313A_ID] = "AXP313a",
+ [AXP323_ID] = "AXP323",
+ [AXP717_ID] = "AXP717",
+ [AXP803_ID] = "AXP803",
+ [AXP806_ID] = "AXP806",
+ [AXP809_ID] = "AXP809",
+ [AXP813_ID] = "AXP813",
+ [AXP15060_ID] = "AXP15060",
};
static const struct regmap_range axp152_writeable_ranges[] = {
@@ -191,6 +194,10 @@ static const struct regmap_range axp313a_writeable_ranges[] = {
regmap_reg_range(AXP313A_ON_INDICATE, AXP313A_IRQ_STATE),
};
+static const struct regmap_range axp323_writeable_ranges[] = {
+ regmap_reg_range(AXP313A_ON_INDICATE, AXP323_DCDC_MODE_CTRL2),
+};
+
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),
@@ -201,11 +208,47 @@ static const struct regmap_access_table axp313a_writeable_table = {
.n_yes_ranges = ARRAY_SIZE(axp313a_writeable_ranges),
};
+static const struct regmap_access_table axp323_writeable_table = {
+ .yes_ranges = axp323_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp323_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 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),
+ regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF),
+ regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN),
+ regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_TS_PIN_CFG, AXP717_TS_PIN_CFG),
+ regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET),
+ regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL),
+ regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL),
+ regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL),
+};
+
+static const struct regmap_range axp717_volatile_ranges[] = {
+ regmap_reg_range(AXP717_ON_INDICATE, AXP717_PMU_FAULT),
+ regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA),
+ regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L),
+ regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L),
+};
+
+static const struct regmap_access_table axp717_writeable_table = {
+ .yes_ranges = axp717_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp717_writeable_ranges),
+};
+
+static const struct regmap_access_table axp717_volatile_table = {
+ .yes_ranges = axp717_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp717_volatile_ranges),
+};
+
static const struct regmap_range axp806_volatile_ranges[] = {
regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
};
@@ -286,6 +329,12 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
};
+static const struct resource axp717_usb_power_supply_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_OVER_V, "VBUS_OVER_V"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
+};
+
/* AXP803 and AXP813/AXP818 share the same interrupts */
static const struct resource axp803_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
@@ -316,6 +365,11 @@ static const struct resource axp313a_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP313A_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"),
+};
+
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"),
@@ -342,7 +396,7 @@ static const struct regmap_config axp152_regmap_config = {
.wr_table = &axp152_writeable_table,
.volatile_table = &axp152_volatile_table,
.max_register = AXP152_PWM1_DUTY_CYCLE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp192_regmap_config = {
@@ -351,7 +405,7 @@ static const struct regmap_config axp192_regmap_config = {
.wr_table = &axp192_writeable_table,
.volatile_table = &axp192_volatile_table,
.max_register = AXP20X_CC_CTRL,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp20x_regmap_config = {
@@ -360,7 +414,7 @@ static const struct regmap_config axp20x_regmap_config = {
.wr_table = &axp20x_writeable_table,
.volatile_table = &axp20x_volatile_table,
.max_register = AXP20X_OCV(AXP20X_OCV_MAX),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp22x_regmap_config = {
@@ -369,7 +423,7 @@ static const struct regmap_config axp22x_regmap_config = {
.wr_table = &axp22x_writeable_table,
.volatile_table = &axp22x_volatile_table,
.max_register = AXP22X_BATLOW_THRES1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp288_regmap_config = {
@@ -378,7 +432,7 @@ static const struct regmap_config axp288_regmap_config = {
.wr_table = &axp288_writeable_table,
.volatile_table = &axp288_volatile_table,
.max_register = AXP288_FG_TUNE5,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp313a_regmap_config = {
@@ -387,7 +441,25 @@ static const struct regmap_config axp313a_regmap_config = {
.wr_table = &axp313a_writeable_table,
.volatile_table = &axp313a_volatile_table,
.max_register = AXP313A_IRQ_STATE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config axp323_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp323_writeable_table,
+ .volatile_table = &axp313a_volatile_table,
+ .max_register = AXP323_DCDC_MODE_CTRL2,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config axp717_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp717_writeable_table,
+ .volatile_table = &axp717_volatile_table,
+ .max_register = AXP717_ADC_DATA_L,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp806_regmap_config = {
@@ -396,7 +468,7 @@ static const struct regmap_config axp806_regmap_config = {
.wr_table = &axp806_writeable_table,
.volatile_table = &axp806_volatile_table,
.max_register = AXP806_REG_ADDR_EXT,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp15060_regmap_config = {
@@ -405,7 +477,7 @@ static const struct regmap_config axp15060_regmap_config = {
.wr_table = &axp15060_writeable_table,
.volatile_table = &axp15060_volatile_table,
.max_register = AXP15060_IRQ2_STATE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
@@ -588,6 +660,40 @@ static const struct regmap_irq axp313a_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP313A, DIE_TEMP_HIGH, 0, 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),
+ INIT_REGMAP_IRQ(AXP717, GAUGE_NEW_SOC, 0, 4),
+ INIT_REGMAP_IRQ(AXP717, BOOST_OVER_V, 0, 2),
+ INIT_REGMAP_IRQ(AXP717, VBUS_OVER_V, 0, 1),
+ INIT_REGMAP_IRQ(AXP717, VBUS_FAULT, 0, 0),
+ INIT_REGMAP_IRQ(AXP717, VBUS_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP717, VBUS_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_PLUGIN, 1, 5),
+ INIT_REGMAP_IRQ(AXP717, BATT_REMOVAL, 1, 4),
+ INIT_REGMAP_IRQ(AXP717, PEK_SHORT, 1, 3),
+ INIT_REGMAP_IRQ(AXP717, PEK_LONG, 1, 2),
+ INIT_REGMAP_IRQ(AXP717, PEK_FAL_EDGE, 1, 1),
+ INIT_REGMAP_IRQ(AXP717, PEK_RIS_EDGE, 1, 0),
+ INIT_REGMAP_IRQ(AXP717, WDOG_EXPIRE, 2, 7),
+ INIT_REGMAP_IRQ(AXP717, LDO_OVER_CURR, 2, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_OVER_CURR, 2, 5),
+ INIT_REGMAP_IRQ(AXP717, CHARG_DONE, 2, 4),
+ INIT_REGMAP_IRQ(AXP717, CHARG, 2, 3),
+ INIT_REGMAP_IRQ(AXP717, DIE_TEMP_HIGH, 2, 2),
+ INIT_REGMAP_IRQ(AXP717, CHARG_TIMER, 2, 1),
+ INIT_REGMAP_IRQ(AXP717, BATT_OVER_V, 2, 0),
+ INIT_REGMAP_IRQ(AXP717, BC_USB_DONE, 3, 7),
+ INIT_REGMAP_IRQ(AXP717, BC_USB_CHNG, 3, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_QUIT_TEMP_HIGH, 3, 4),
+ INIT_REGMAP_IRQ(AXP717, BATT_CHG_TEMP_HIGH, 3, 3),
+ INIT_REGMAP_IRQ(AXP717, BATT_CHG_TEMP_LOW, 3, 2),
+ INIT_REGMAP_IRQ(AXP717, BATT_ACT_TEMP_HIGH, 3, 1),
+ INIT_REGMAP_IRQ(AXP717, BATT_ACT_TEMP_LOW, 3, 0),
+ INIT_REGMAP_IRQ(AXP717, TYPEC_REMOVE, 4, 6),
+ INIT_REGMAP_IRQ(AXP717, TYPEC_PLUGIN, 4, 5),
+};
+
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),
@@ -775,6 +881,17 @@ static const struct regmap_irq_chip axp313a_regmap_irq_chip = {
.num_regs = 1,
};
+static const struct regmap_irq_chip axp717_regmap_irq_chip = {
+ .name = "axp717_irq_chip",
+ .status_base = AXP717_IRQ0_STATE,
+ .ack_base = AXP717_IRQ0_STATE,
+ .unmask_base = AXP717_IRQ0_EN,
+ .init_ack_masked = true,
+ .irqs = axp717_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp717_regmap_irqs),
+ .num_regs = 5,
+};
+
static const struct regmap_irq_chip axp803_regmap_irq_chip = {
.name = "axp803",
.status_base = AXP20X_IRQ1_STATE,
@@ -936,10 +1053,23 @@ static const struct mfd_cell axp152_cells[] = {
};
static struct mfd_cell axp313a_cells[] = {
- MFD_CELL_NAME("axp20x-regulator"),
+ /* AXP323 is sometimes paired with AXP717 as sub-PMIC */
+ MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),
MFD_CELL_RES("axp313a-pek", axp313a_pek_resources),
};
+static struct mfd_cell axp717_cells[] = {
+ MFD_CELL_NAME("axp20x-regulator"),
+ MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
+ MFD_CELL_OF("axp717-adc",
+ NULL, NULL, 0, 0, "x-powers,axp717-adc"),
+ MFD_CELL_OF("axp20x-usb-power-supply",
+ axp717_usb_power_supply_resources, NULL, 0, 0,
+ "x-powers,axp717-usb-power-supply"),
+ MFD_CELL_OF("axp20x-battery-power-supply",
+ NULL, NULL, 0, 0, "x-powers,axp717-battery-power-supply"),
+};
+
static const struct resource axp288_adc_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP288_IRQ_GPADC, "GPADC"),
};
@@ -1101,9 +1231,8 @@ static const struct mfd_cell axp15060_cells[] = {
/* For boards that don't have IRQ line connected to SOC. */
static const struct mfd_cell axp_regulator_only_cells[] = {
- {
- .name = "axp20x-regulator",
- },
+ /* PMIC without IRQ line may be secondary PMIC */
+ MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),
};
static int axp20x_power_off(struct sys_off_data *data)
@@ -1112,6 +1241,7 @@ static int axp20x_power_off(struct sys_off_data *data)
unsigned int shutdown_reg;
switch (axp20x->variant) {
+ case AXP323_ID:
case AXP313A_ID:
shutdown_reg = AXP313A_SHUTDOWN_CTRL;
break;
@@ -1131,25 +1261,10 @@ static int axp20x_power_off(struct sys_off_data *data)
int axp20x_match_device(struct axp20x_dev *axp20x)
{
struct device *dev = axp20x->dev;
- const struct acpi_device_id *acpi_id;
- const struct of_device_id *of_id;
-
- if (dev->of_node) {
- of_id = of_match_device(dev->driver->of_match_table, dev);
- if (!of_id) {
- dev_err(dev, "Unable to match OF ID\n");
- return -ENODEV;
- }
- axp20x->variant = (long)of_id->data;
- } else {
- acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!acpi_id || !acpi_id->driver_data) {
- dev_err(dev, "Unable to match ACPI ID and data\n");
- return -ENODEV;
- }
- axp20x->variant = (long)acpi_id->driver_data;
- }
+ const struct mfd_cell *cells_no_irq = NULL;
+ int nr_cells_no_irq = 0;
+ axp20x->variant = (long)device_get_match_data(dev);
switch (axp20x->variant) {
case AXP152_ID:
axp20x->nr_cells = ARRAY_SIZE(axp152_cells);
@@ -1195,6 +1310,18 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_cfg = &axp313a_regmap_config;
axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip;
break;
+ case AXP323_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp313a_cells);
+ axp20x->cells = axp313a_cells;
+ axp20x->regmap_cfg = &axp323_regmap_config;
+ axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip;
+ break;
+ case AXP717_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp717_cells);
+ axp20x->cells = axp717_cells;
+ axp20x->regmap_cfg = &axp717_regmap_config;
+ axp20x->regmap_irq_chip = &axp717_regmap_irq_chip;
+ break;
case AXP803_ID:
axp20x->nr_cells = ARRAY_SIZE(axp803_cells);
axp20x->cells = axp803_cells;
@@ -1207,14 +1334,15 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
* if there is no interrupt line.
*/
if (of_property_read_bool(axp20x->dev->of_node,
- "x-powers,self-working-mode") &&
- axp20x->irq > 0) {
+ "x-powers,self-working-mode")) {
axp20x->nr_cells = ARRAY_SIZE(axp806_self_working_cells);
axp20x->cells = axp806_self_working_cells;
} else {
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
axp20x->cells = axp806_cells;
}
+ nr_cells_no_irq = ARRAY_SIZE(axp806_cells);
+ cells_no_irq = axp806_cells;
axp20x->regmap_cfg = &axp806_regmap_config;
axp20x->regmap_irq_chip = &axp806_regmap_irq_chip;
break;
@@ -1238,31 +1366,32 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_irq_chip = &axp803_regmap_irq_chip;
break;
case AXP15060_ID:
- /*
- * Don't register the power key part if there is no interrupt
- * line.
- *
- * Since most use cases of AXP PMICs are Allwinner SOCs, board
- * designers follow Allwinner's reference design and connects
- * IRQ line to SOC, there's no need for those variants to deal
- * with cases that IRQ isn't connected. However, AXP15660 is
- * used by some other vendors' SOCs that didn't connect IRQ
- * line, we need to deal with this case.
- */
- if (axp20x->irq > 0) {
- axp20x->nr_cells = ARRAY_SIZE(axp15060_cells);
- axp20x->cells = axp15060_cells;
- } else {
- axp20x->nr_cells = ARRAY_SIZE(axp_regulator_only_cells);
- axp20x->cells = axp_regulator_only_cells;
- }
+ axp20x->nr_cells = ARRAY_SIZE(axp15060_cells);
+ axp20x->cells = axp15060_cells;
axp20x->regmap_cfg = &axp15060_regmap_config;
axp20x->regmap_irq_chip = &axp15060_regmap_irq_chip;
break;
default:
- dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant);
+ dev_err(dev, "unsupported AXP20X ID %u\n", axp20x->variant);
return -EINVAL;
}
+
+ /*
+ * Use an alternative cell array when no interrupt line is connected,
+ * since IRQs are required by some drivers.
+ * The default is the safe "regulator-only", as this works fine without
+ * an interrupt specified.
+ */
+ if (axp20x->irq <= 0) {
+ if (cells_no_irq) {
+ axp20x->nr_cells = nr_cells_no_irq;
+ axp20x->cells = cells_no_irq;
+ } else {
+ axp20x->nr_cells = ARRAY_SIZE(axp_regulator_only_cells);
+ axp20x->cells = axp_regulator_only_cells;
+ }
+ }
+
dev_info(dev, "AXP20x variant %s found\n",
axp20x_model_names[axp20x->variant]);
@@ -1317,7 +1446,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
}
}
- ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells,
+ ret = mfd_add_devices(axp20x->dev, PLATFORM_DEVID_NONE, axp20x->cells,
axp20x->nr_cells, NULL, 0, NULL);
if (ret) {
@@ -1327,10 +1456,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
}
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);
+ devm_register_power_off_handler(axp20x->dev, axp20x_power_off, axp20x);
dev_info(axp20x->dev, "AXP20X driver loaded\n");