drivers/mfd/mfd-core.c

Source file repositories/reference/linux-study-clean/drivers/mfd/mfd-core.c

File Facts

System
Linux kernel
Corpus path
drivers/mfd/mfd-core.c
Extension
.c
Size
11794 bytes
Lines
456
Domain
Driver Families
Bucket
drivers/mfd
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mfd_of_node_entry {
	struct list_head list;
	struct device *dev;
	struct device_node *np;
};

static const struct device_type mfd_dev_type = {
	.name	= "mfd_device",
};

#if IS_ENABLED(CONFIG_ACPI)
struct match_ids_walk_data {
	struct acpi_device_id *ids;
	struct acpi_device *adev;
};

static int match_device_ids(struct acpi_device *adev, void *data)
{
	struct match_ids_walk_data *wd = data;

	if (!acpi_match_device_ids(adev, wd->ids)) {
		wd->adev = adev;
		return 1;
	}

	return 0;
}

static void mfd_acpi_add_device(const struct mfd_cell *cell,
				struct platform_device *pdev)
{
	const struct mfd_cell_acpi_match *match = cell->acpi_match;
	struct acpi_device *adev = NULL;
	struct acpi_device *parent;

	parent = ACPI_COMPANION(pdev->dev.parent);
	if (!parent)
		return;

	/*
	 * MFD child device gets its ACPI handle either from the ACPI device
	 * directly under the parent that matches the either _HID or _CID, or
	 * _ADR or it will use the parent handle if is no ID is given.
	 *
	 * Note that use of _ADR is a grey area in the ACPI specification,
	 * though at least Intel Galileo Gen 2 is using it to distinguish
	 * the children devices.
	 */
	if (match) {
		if (match->pnpid) {
			struct acpi_device_id ids[2] = {};
			struct match_ids_walk_data wd = {
				.adev = NULL,
				.ids = ids,
			};

			strscpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
			acpi_dev_for_each_child(parent, match_device_ids, &wd);
			adev = wd.adev;
		} else {
			adev = acpi_find_child_device(parent, match->adr, false);
		}
	}

	/*
	 * NOTE: The fwnode design doesn't allow proper stacking/sharing. This
	 * should eventually turn into a device fwnode API call that will allow
	 * prepending to a list of fwnodes (with ACPI taking precedence).
	 *
	 * set_primary_fwnode() is used here, instead of device_set_node(), as
	 * device_set_node() will overwrite the existing fwnode, which may be an
	 * OF node that was populated earlier. To support a use case where ACPI
	 * and OF is used in conjunction, we call set_primary_fwnode() instead.
	 */
	set_primary_fwnode(&pdev->dev, acpi_fwnode_handle(adev ?: parent));
}
#else
static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
				       struct platform_device *pdev)
{
}
#endif

static int mfd_match_of_node_to_dev(struct platform_device *pdev,
				    struct device_node *np,
				    const struct mfd_cell *cell)
{
	struct mfd_of_node_entry *of_entry;
	u64 of_node_addr;

Annotation

Implementation Notes