162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mdio-boardinfo - Collect pre-declarations for MDIO devices
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/export.h>
962306a36Sopenharmony_ci#include <linux/mutex.h>
1062306a36Sopenharmony_ci#include <linux/list.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "mdio-boardinfo.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic LIST_HEAD(mdio_board_list);
1562306a36Sopenharmony_cistatic DEFINE_MUTEX(mdio_board_lock);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * mdiobus_setup_mdiodev_from_board_info - create and setup MDIO devices
1962306a36Sopenharmony_ci * from pre-collected board specific MDIO information
2062306a36Sopenharmony_ci * @bus: Bus the board_info belongs to
2162306a36Sopenharmony_ci * @cb: Callback to create device on bus
2262306a36Sopenharmony_ci * Context: can sleep
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_civoid mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus,
2562306a36Sopenharmony_ci					   int (*cb)
2662306a36Sopenharmony_ci					   (struct mii_bus *bus,
2762306a36Sopenharmony_ci					    struct mdio_board_info *bi))
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct mdio_board_entry *be;
3062306a36Sopenharmony_ci	struct mdio_board_entry *tmp;
3162306a36Sopenharmony_ci	struct mdio_board_info *bi;
3262306a36Sopenharmony_ci	int ret;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	mutex_lock(&mdio_board_lock);
3562306a36Sopenharmony_ci	list_for_each_entry_safe(be, tmp, &mdio_board_list, list) {
3662306a36Sopenharmony_ci		bi = &be->board_info;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		if (strcmp(bus->id, bi->bus_id))
3962306a36Sopenharmony_ci			continue;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		mutex_unlock(&mdio_board_lock);
4262306a36Sopenharmony_ci		ret = cb(bus, bi);
4362306a36Sopenharmony_ci		mutex_lock(&mdio_board_lock);
4462306a36Sopenharmony_ci		if (ret)
4562306a36Sopenharmony_ci			continue;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci	mutex_unlock(&mdio_board_lock);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobus_setup_mdiodev_from_board_info);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * mdiobus_register_board_info - register MDIO devices for a given board
5462306a36Sopenharmony_ci * @info: array of devices descriptors
5562306a36Sopenharmony_ci * @n: number of descriptors provided
5662306a36Sopenharmony_ci * Context: can sleep
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * The board info passed can be marked with __initdata but be pointers
5962306a36Sopenharmony_ci * such as platform_data etc. are copied as-is
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ciint mdiobus_register_board_info(const struct mdio_board_info *info,
6262306a36Sopenharmony_ci				unsigned int n)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct mdio_board_entry *be;
6562306a36Sopenharmony_ci	unsigned int i;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	be = kcalloc(n, sizeof(*be), GFP_KERNEL);
6862306a36Sopenharmony_ci	if (!be)
6962306a36Sopenharmony_ci		return -ENOMEM;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	for (i = 0; i < n; i++, be++, info++) {
7262306a36Sopenharmony_ci		memcpy(&be->board_info, info, sizeof(*info));
7362306a36Sopenharmony_ci		mutex_lock(&mdio_board_lock);
7462306a36Sopenharmony_ci		list_add_tail(&be->list, &mdio_board_list);
7562306a36Sopenharmony_ci		mutex_unlock(&mdio_board_lock);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobus_register_board_info);
81