2020-06-16 14:08:52

by Charles Keepax

[permalink] [raw]
Subject: [PATCH v3 1/2] mfd: mfd-core: Add mechanism for removal of a subset of children

Currently, the only way to remove MFD children is with a call to
mfd_remove_devices, which will remove all the children. Under
some circumstances it is useful to remove only a subset of the
child devices. For example if some additional clean up is required
between removal of certain child devices.

To accomplish this a level field is added to mfd_cell, the normal
mfd_remove_devices is modified to not remove devices that are set
to a higher level and a corresponding mfd_remove_devices_late
function is added to remove those children.

See further discussion at:
https://lore.kernel.org/lkml/20200616075834.GF2608702@dell/

Suggested-by: Lee Jones <[email protected]>
Signed-off-by: Charles Keepax <[email protected]>
---

Changes since v2:
- Removed old tag system and now just have 2 levels, one removed
by new mfd_remove_devices_late

Thanks,
Charles

drivers/mfd/mfd-core.c | 16 +++++++++++++++-
include/linux/mfd/core.h | 5 +++++
2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index f5a73af60dd40..d1602a87db29b 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -287,6 +287,7 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
{
struct platform_device *pdev;
const struct mfd_cell *cell;
+ int *level = data;

if (dev->type != &mfd_dev_type)
return 0;
@@ -294,6 +295,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
pdev = to_platform_device(dev);
cell = mfd_get_cell(pdev);

+ if (level && cell->level > *level)
+ return 0;
+
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);

@@ -301,9 +305,19 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
return 0;
}

+void mfd_remove_devices_late(struct device *parent)
+{
+ int level = MFD_DEP_LEVEL_HIGH;
+
+ device_for_each_child_reverse(parent, &level, mfd_remove_devices_fn);
+}
+EXPORT_SYMBOL(mfd_remove_devices_late);
+
void mfd_remove_devices(struct device *parent)
{
- device_for_each_child_reverse(parent, NULL, mfd_remove_devices_fn);
+ int level = MFD_DEP_LEVEL_NORMAL;
+
+ device_for_each_child_reverse(parent, &level, mfd_remove_devices_fn);
}
EXPORT_SYMBOL(mfd_remove_devices);

diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index 7e5ac3c00891a..1ca2048eba22b 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -41,6 +41,9 @@
#define MFD_CELL_NAME(_name) \
MFD_CELL_ALL(_name, NULL, NULL, 0, 0, NULL, NULL) \

+#define MFD_DEP_LEVEL_NORMAL 0
+#define MFD_DEP_LEVEL_HIGH 1
+
struct irq_domain;
struct property_entry;

@@ -58,6 +61,7 @@ struct mfd_cell_acpi_match {
struct mfd_cell {
const char *name;
int id;
+ int level;

int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev);
@@ -135,6 +139,7 @@ static inline int mfd_add_hotplug_devices(struct device *parent,
}

extern void mfd_remove_devices(struct device *parent);
+extern void mfd_remove_devices_late(struct device *parent);

extern int devm_mfd_add_devices(struct device *dev, int id,
const struct mfd_cell *cells, int n_devs,
--
2.11.0


2020-06-16 14:08:54

by Charles Keepax

[permalink] [raw]
Subject: [PATCH v3 2/2] mfd: madera: Improve handling of regulator unbinding

The current unbinding process for Madera has some issues. The trouble
is runtime PM is disabled as the first step of the process, but
some of the drivers release IRQs causing regmap IRQ to issue a
runtime get which fails. To allow runtime PM to remain enabled during
mfd_remove_devices, the DCVDD regulator must remain available. In
the case of external DCVDD's this is simple, the regulator can simply
be disabled/put after the call to mfd_remove_devices. However, in
the case of an internally supplied DCVDD the regulator needs to be
released after the other MFD children depending on it.

Use the new MFD mfd_remove_devices_late functionality to split
the DCVDD regulator off from the other drivers.

Signed-off-by: Charles Keepax <[email protected]>
---

Changes since v2:
- Moved to new mfd_remove_devices_late

Thanks,
Charles

drivers/mfd/madera-core.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/mfd/madera-core.c b/drivers/mfd/madera-core.c
index 4724c1a01a39f..8a8d733fdce5b 100644
--- a/drivers/mfd/madera-core.c
+++ b/drivers/mfd/madera-core.c
@@ -44,7 +44,10 @@ static const char * const madera_core_supplies[] = {
};

static const struct mfd_cell madera_ldo1_devs[] = {
- { .name = "madera-ldo1", },
+ {
+ .name = "madera-ldo1",
+ .level = MFD_DEP_LEVEL_HIGH,
+ },
};

static const char * const cs47l15_supplies[] = {
@@ -743,18 +746,22 @@ int madera_dev_exit(struct madera *madera)
/* Prevent any IRQs being serviced while we clean up */
disable_irq(madera->irq);

- /*
- * DCVDD could be supplied by a child node, we must disable it before
- * removing the children, and prevent PM runtime from turning it back on
- */
- pm_runtime_disable(madera->dev);
+ pm_runtime_get_sync(madera->dev);

- clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk);
+ mfd_remove_devices(madera->dev);
+
+ pm_runtime_disable(madera->dev);

regulator_disable(madera->dcvdd);
regulator_put(madera->dcvdd);

- mfd_remove_devices(madera->dev);
+ mfd_remove_devices_late(madera->dev);
+
+ pm_runtime_set_suspended(madera->dev);
+ pm_runtime_put_noidle(madera->dev);
+
+ clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk);
+
madera_enable_hard_reset(madera);

regulator_bulk_disable(madera->num_core_supplies,
--
2.11.0