162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom UniMAC MDIO bus controller driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2017 Broadcom
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/of_mdio.h>
1562306a36Sopenharmony_ci#include <linux/of_platform.h>
1662306a36Sopenharmony_ci#include <linux/phy.h>
1762306a36Sopenharmony_ci#include <linux/platform_data/mdio-bcm-unimac.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/sched.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define MDIO_CMD		0x00
2262306a36Sopenharmony_ci#define  MDIO_START_BUSY	(1 << 29)
2362306a36Sopenharmony_ci#define  MDIO_READ_FAIL		(1 << 28)
2462306a36Sopenharmony_ci#define  MDIO_RD		(2 << 26)
2562306a36Sopenharmony_ci#define  MDIO_WR		(1 << 26)
2662306a36Sopenharmony_ci#define  MDIO_PMD_SHIFT		21
2762306a36Sopenharmony_ci#define  MDIO_PMD_MASK		0x1F
2862306a36Sopenharmony_ci#define  MDIO_REG_SHIFT		16
2962306a36Sopenharmony_ci#define  MDIO_REG_MASK		0x1F
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MDIO_CFG		0x04
3262306a36Sopenharmony_ci#define  MDIO_C22		(1 << 0)
3362306a36Sopenharmony_ci#define  MDIO_C45		0
3462306a36Sopenharmony_ci#define  MDIO_CLK_DIV_SHIFT	4
3562306a36Sopenharmony_ci#define  MDIO_CLK_DIV_MASK	0x3F
3662306a36Sopenharmony_ci#define  MDIO_SUPP_PREAMBLE	(1 << 12)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct unimac_mdio_priv {
3962306a36Sopenharmony_ci	struct mii_bus		*mii_bus;
4062306a36Sopenharmony_ci	void __iomem		*base;
4162306a36Sopenharmony_ci	int (*wait_func)	(void *wait_func_data);
4262306a36Sopenharmony_ci	void			*wait_func_data;
4362306a36Sopenharmony_ci	struct clk		*clk;
4462306a36Sopenharmony_ci	u32			clk_freq;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	/* MIPS chips strapped for BE will automagically configure the
5062306a36Sopenharmony_ci	 * peripheral registers for CPU-native byte order.
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
5362306a36Sopenharmony_ci		return __raw_readl(priv->base + offset);
5462306a36Sopenharmony_ci	else
5562306a36Sopenharmony_ci		return readl_relaxed(priv->base + offset);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic inline void unimac_mdio_writel(struct unimac_mdio_priv *priv, u32 val,
5962306a36Sopenharmony_ci				      u32 offset)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
6262306a36Sopenharmony_ci		__raw_writel(val, priv->base + offset);
6362306a36Sopenharmony_ci	else
6462306a36Sopenharmony_ci		writel_relaxed(val, priv->base + offset);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic inline void unimac_mdio_start(struct unimac_mdio_priv *priv)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	u32 reg;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	reg = unimac_mdio_readl(priv, MDIO_CMD);
7262306a36Sopenharmony_ci	reg |= MDIO_START_BUSY;
7362306a36Sopenharmony_ci	unimac_mdio_writel(priv, reg, MDIO_CMD);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return unimac_mdio_readl(priv, MDIO_CMD) & MDIO_START_BUSY;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int unimac_mdio_poll(void *wait_func_data)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = wait_func_data;
8462306a36Sopenharmony_ci	unsigned int timeout = 1000;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	do {
8762306a36Sopenharmony_ci		if (!unimac_mdio_busy(priv))
8862306a36Sopenharmony_ci			return 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		usleep_range(1000, 2000);
9162306a36Sopenharmony_ci	} while (--timeout);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return -ETIMEDOUT;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = bus->priv;
9962306a36Sopenharmony_ci	int ret;
10062306a36Sopenharmony_ci	u32 cmd;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Prepare the read operation */
10362306a36Sopenharmony_ci	cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
10462306a36Sopenharmony_ci	unimac_mdio_writel(priv, cmd, MDIO_CMD);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Start MDIO transaction */
10762306a36Sopenharmony_ci	unimac_mdio_start(priv);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = priv->wait_func(priv->wait_func_data);
11062306a36Sopenharmony_ci	if (ret)
11162306a36Sopenharmony_ci		return ret;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	cmd = unimac_mdio_readl(priv, MDIO_CMD);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Some broken devices are known not to release the line during
11662306a36Sopenharmony_ci	 * turn-around, e.g: Broadcom BCM53125 external switches, so check for
11762306a36Sopenharmony_ci	 * that condition here and ignore the MDIO controller read failure
11862306a36Sopenharmony_ci	 * indication.
11962306a36Sopenharmony_ci	 */
12062306a36Sopenharmony_ci	if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL))
12162306a36Sopenharmony_ci		return -EIO;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return cmd & 0xffff;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int unimac_mdio_write(struct mii_bus *bus, int phy_id,
12762306a36Sopenharmony_ci			     int reg, u16 val)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = bus->priv;
13062306a36Sopenharmony_ci	u32 cmd;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Prepare the write operation */
13362306a36Sopenharmony_ci	cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
13462306a36Sopenharmony_ci		(reg << MDIO_REG_SHIFT) | (0xffff & val);
13562306a36Sopenharmony_ci	unimac_mdio_writel(priv, cmd, MDIO_CMD);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	unimac_mdio_start(priv);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return priv->wait_func(priv->wait_func_data);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with
14362306a36Sopenharmony_ci * their internal MDIO management controller making them fail to successfully
14462306a36Sopenharmony_ci * be read from or written to for the first transaction.  We insert a dummy
14562306a36Sopenharmony_ci * BMSR read here to make sure that phy_get_device() and get_phy_id() can
14662306a36Sopenharmony_ci * correctly read the PHY MII_PHYSID1/2 registers and successfully register a
14762306a36Sopenharmony_ci * PHY device for this peripheral.
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * Once the PHY driver is registered, we can workaround subsequent reads from
15062306a36Sopenharmony_ci * there (e.g: during system-wide power management).
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * bus->reset is invoked before mdiobus_scan during mdiobus_register and is
15362306a36Sopenharmony_ci * therefore the right location to stick that workaround. Since we do not want
15462306a36Sopenharmony_ci * to read from non-existing PHYs, we either use bus->phy_mask or do a manual
15562306a36Sopenharmony_ci * Device Tree scan to limit the search area.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic int unimac_mdio_reset(struct mii_bus *bus)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct device_node *np = bus->dev.of_node;
16062306a36Sopenharmony_ci	struct device_node *child;
16162306a36Sopenharmony_ci	u32 read_mask = 0;
16262306a36Sopenharmony_ci	int addr;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!np) {
16562306a36Sopenharmony_ci		read_mask = ~bus->phy_mask;
16662306a36Sopenharmony_ci	} else {
16762306a36Sopenharmony_ci		for_each_available_child_of_node(np, child) {
16862306a36Sopenharmony_ci			addr = of_mdio_parse_addr(&bus->dev, child);
16962306a36Sopenharmony_ci			if (addr < 0)
17062306a36Sopenharmony_ci				continue;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci			read_mask |= 1 << addr;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
17762306a36Sopenharmony_ci		if (read_mask & 1 << addr) {
17862306a36Sopenharmony_ci			dev_dbg(&bus->dev, "Workaround for PHY @ %d\n", addr);
17962306a36Sopenharmony_ci			mdiobus_read(bus, addr, MII_BMSR);
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	unsigned long rate;
18962306a36Sopenharmony_ci	u32 reg, div;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Keep the hardware default values */
19262306a36Sopenharmony_ci	if (!priv->clk_freq)
19362306a36Sopenharmony_ci		return;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (!priv->clk)
19662306a36Sopenharmony_ci		rate = 250000000;
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		rate = clk_get_rate(priv->clk);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	div = (rate / (2 * priv->clk_freq)) - 1;
20162306a36Sopenharmony_ci	if (div & ~MDIO_CLK_DIV_MASK) {
20262306a36Sopenharmony_ci		pr_warn("Incorrect MDIO clock frequency, ignoring\n");
20362306a36Sopenharmony_ci		return;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* The MDIO clock is the reference clock (typically 250Mhz) divided by
20762306a36Sopenharmony_ci	 * 2 x (MDIO_CLK_DIV + 1)
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	reg = unimac_mdio_readl(priv, MDIO_CFG);
21062306a36Sopenharmony_ci	reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
21162306a36Sopenharmony_ci	reg |= div << MDIO_CLK_DIV_SHIFT;
21262306a36Sopenharmony_ci	unimac_mdio_writel(priv, reg, MDIO_CFG);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int unimac_mdio_probe(struct platform_device *pdev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
21862306a36Sopenharmony_ci	struct unimac_mdio_priv *priv;
21962306a36Sopenharmony_ci	struct device_node *np;
22062306a36Sopenharmony_ci	struct mii_bus *bus;
22162306a36Sopenharmony_ci	struct resource *r;
22262306a36Sopenharmony_ci	int ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	np = pdev->dev.of_node;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
22762306a36Sopenharmony_ci	if (!priv)
22862306a36Sopenharmony_ci		return -ENOMEM;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
23162306a36Sopenharmony_ci	if (!r)
23262306a36Sopenharmony_ci		return -EINVAL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Just ioremap, as this MDIO block is usually integrated into an
23562306a36Sopenharmony_ci	 * Ethernet MAC controller register range
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
23862306a36Sopenharmony_ci	if (!priv->base) {
23962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to remap register\n");
24062306a36Sopenharmony_ci		return -ENOMEM;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	priv->clk = devm_clk_get_optional(&pdev->dev, NULL);
24462306a36Sopenharmony_ci	if (IS_ERR(priv->clk))
24562306a36Sopenharmony_ci		return PTR_ERR(priv->clk);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
24862306a36Sopenharmony_ci	if (ret)
24962306a36Sopenharmony_ci		return ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
25262306a36Sopenharmony_ci		priv->clk_freq = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	unimac_mdio_clk_set(priv);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	priv->mii_bus = mdiobus_alloc();
25762306a36Sopenharmony_ci	if (!priv->mii_bus) {
25862306a36Sopenharmony_ci		ret = -ENOMEM;
25962306a36Sopenharmony_ci		goto out_clk_disable;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	bus = priv->mii_bus;
26362306a36Sopenharmony_ci	bus->priv = priv;
26462306a36Sopenharmony_ci	if (pdata) {
26562306a36Sopenharmony_ci		bus->name = pdata->bus_name;
26662306a36Sopenharmony_ci		priv->wait_func = pdata->wait_func;
26762306a36Sopenharmony_ci		priv->wait_func_data = pdata->wait_func_data;
26862306a36Sopenharmony_ci		bus->phy_mask = ~pdata->phy_mask;
26962306a36Sopenharmony_ci	} else {
27062306a36Sopenharmony_ci		bus->name = "unimac MII bus";
27162306a36Sopenharmony_ci		priv->wait_func_data = priv;
27262306a36Sopenharmony_ci		priv->wait_func = unimac_mdio_poll;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	bus->parent = &pdev->dev;
27562306a36Sopenharmony_ci	bus->read = unimac_mdio_read;
27662306a36Sopenharmony_ci	bus->write = unimac_mdio_write;
27762306a36Sopenharmony_ci	bus->reset = unimac_mdio_reset;
27862306a36Sopenharmony_ci	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ret = of_mdiobus_register(bus, np);
28162306a36Sopenharmony_ci	if (ret) {
28262306a36Sopenharmony_ci		dev_err(&pdev->dev, "MDIO bus registration failed\n");
28362306a36Sopenharmony_ci		goto out_mdio_free;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus\n");
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ciout_mdio_free:
29362306a36Sopenharmony_ci	mdiobus_free(bus);
29462306a36Sopenharmony_ciout_clk_disable:
29562306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
29662306a36Sopenharmony_ci	return ret;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int unimac_mdio_remove(struct platform_device *pdev)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	mdiobus_unregister(priv->mii_bus);
30462306a36Sopenharmony_ci	mdiobus_free(priv->mii_bus);
30562306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int __maybe_unused unimac_mdio_suspend(struct device *d)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = dev_get_drvdata(d);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int __maybe_unused unimac_mdio_resume(struct device *d)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct unimac_mdio_priv *priv = dev_get_drvdata(d);
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
32562306a36Sopenharmony_ci	if (ret)
32662306a36Sopenharmony_ci		return ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	unimac_mdio_clk_set(priv);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
33462306a36Sopenharmony_ci			 unimac_mdio_suspend, unimac_mdio_resume);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic const struct of_device_id unimac_mdio_ids[] = {
33762306a36Sopenharmony_ci	{ .compatible = "brcm,asp-v2.1-mdio", },
33862306a36Sopenharmony_ci	{ .compatible = "brcm,asp-v2.0-mdio", },
33962306a36Sopenharmony_ci	{ .compatible = "brcm,genet-mdio-v5", },
34062306a36Sopenharmony_ci	{ .compatible = "brcm,genet-mdio-v4", },
34162306a36Sopenharmony_ci	{ .compatible = "brcm,genet-mdio-v3", },
34262306a36Sopenharmony_ci	{ .compatible = "brcm,genet-mdio-v2", },
34362306a36Sopenharmony_ci	{ .compatible = "brcm,genet-mdio-v1", },
34462306a36Sopenharmony_ci	{ .compatible = "brcm,unimac-mdio", },
34562306a36Sopenharmony_ci	{ /* sentinel */ },
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, unimac_mdio_ids);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic struct platform_driver unimac_mdio_driver = {
35062306a36Sopenharmony_ci	.driver = {
35162306a36Sopenharmony_ci		.name = UNIMAC_MDIO_DRV_NAME,
35262306a36Sopenharmony_ci		.of_match_table = unimac_mdio_ids,
35362306a36Sopenharmony_ci		.pm = &unimac_mdio_pm_ops,
35462306a36Sopenharmony_ci	},
35562306a36Sopenharmony_ci	.probe	= unimac_mdio_probe,
35662306a36Sopenharmony_ci	.remove	= unimac_mdio_remove,
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_cimodule_platform_driver(unimac_mdio_driver);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation");
36162306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller");
36262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
36362306a36Sopenharmony_ciMODULE_ALIAS("platform:" UNIMAC_MDIO_DRV_NAME);
364