18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Texas Instruments CPSW Port's PHY Interface Mode selection Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on cpsw-phy-sel.c driver created by Mugunthan V N <mugunthanvnm@ti.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/of_net.h>
168c2ecf20Sopenharmony_ci#include <linux/phy.h>
178c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
188c2ecf20Sopenharmony_ci#include <linux/regmap.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* AM33xx SoC specific definitions for the CONTROL port */
218c2ecf20Sopenharmony_ci#define AM33XX_GMII_SEL_MODE_MII	0
228c2ecf20Sopenharmony_ci#define AM33XX_GMII_SEL_MODE_RMII	1
238c2ecf20Sopenharmony_ci#define AM33XX_GMII_SEL_MODE_RGMII	2
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cienum {
268c2ecf20Sopenharmony_ci	PHY_GMII_SEL_PORT_MODE = 0,
278c2ecf20Sopenharmony_ci	PHY_GMII_SEL_RGMII_ID_MODE,
288c2ecf20Sopenharmony_ci	PHY_GMII_SEL_RMII_IO_CLK_EN,
298c2ecf20Sopenharmony_ci	PHY_GMII_SEL_LAST,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct phy_gmii_sel_phy_priv {
338c2ecf20Sopenharmony_ci	struct phy_gmii_sel_priv *priv;
348c2ecf20Sopenharmony_ci	u32		id;
358c2ecf20Sopenharmony_ci	struct phy	*if_phy;
368c2ecf20Sopenharmony_ci	int		rmii_clock_external;
378c2ecf20Sopenharmony_ci	int		phy_if_mode;
388c2ecf20Sopenharmony_ci	struct regmap_field *fields[PHY_GMII_SEL_LAST];
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct phy_gmii_sel_soc_data {
428c2ecf20Sopenharmony_ci	u32 num_ports;
438c2ecf20Sopenharmony_ci	u32 features;
448c2ecf20Sopenharmony_ci	const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
458c2ecf20Sopenharmony_ci	bool use_of_data;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct phy_gmii_sel_priv {
498c2ecf20Sopenharmony_ci	struct device *dev;
508c2ecf20Sopenharmony_ci	const struct phy_gmii_sel_soc_data *soc_data;
518c2ecf20Sopenharmony_ci	struct regmap *regmap;
528c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
538c2ecf20Sopenharmony_ci	struct phy_gmii_sel_phy_priv *if_phys;
548c2ecf20Sopenharmony_ci	u32 num_ports;
558c2ecf20Sopenharmony_ci	u32 reg_offset;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct phy_gmii_sel_phy_priv *if_phy = phy_get_drvdata(phy);
618c2ecf20Sopenharmony_ci	const struct phy_gmii_sel_soc_data *soc_data = if_phy->priv->soc_data;
628c2ecf20Sopenharmony_ci	struct device *dev = if_phy->priv->dev;
638c2ecf20Sopenharmony_ci	struct regmap_field *regfield;
648c2ecf20Sopenharmony_ci	int ret, rgmii_id = 0;
658c2ecf20Sopenharmony_ci	u32 gmii_sel_mode = 0;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (mode != PHY_MODE_ETHERNET)
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	switch (submode) {
718c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_RMII:
728c2ecf20Sopenharmony_ci		gmii_sel_mode = AM33XX_GMII_SEL_MODE_RMII;
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
768c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
778c2ecf20Sopenharmony_ci		gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
818c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
828c2ecf20Sopenharmony_ci		gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
838c2ecf20Sopenharmony_ci		rgmii_id = 1;
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_MII:
878c2ecf20Sopenharmony_ci	case PHY_INTERFACE_MODE_GMII:
888c2ecf20Sopenharmony_ci		gmii_sel_mode = AM33XX_GMII_SEL_MODE_MII;
898c2ecf20Sopenharmony_ci		break;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	default:
928c2ecf20Sopenharmony_ci		dev_warn(dev, "port%u: unsupported mode: \"%s\"\n",
938c2ecf20Sopenharmony_ci			 if_phy->id, phy_modes(submode));
948c2ecf20Sopenharmony_ci		return -EINVAL;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if_phy->phy_if_mode = submode;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s id:%u mode:%u rgmii_id:%d rmii_clk_ext:%d\n",
1008c2ecf20Sopenharmony_ci		__func__, if_phy->id, submode, rgmii_id,
1018c2ecf20Sopenharmony_ci		if_phy->rmii_clock_external);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	regfield = if_phy->fields[PHY_GMII_SEL_PORT_MODE];
1048c2ecf20Sopenharmony_ci	ret = regmap_field_write(regfield, gmii_sel_mode);
1058c2ecf20Sopenharmony_ci	if (ret) {
1068c2ecf20Sopenharmony_ci		dev_err(dev, "port%u: set mode fail %d", if_phy->id, ret);
1078c2ecf20Sopenharmony_ci		return ret;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE) &&
1118c2ecf20Sopenharmony_ci	    if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE]) {
1128c2ecf20Sopenharmony_ci		regfield = if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE];
1138c2ecf20Sopenharmony_ci		ret = regmap_field_write(regfield, rgmii_id);
1148c2ecf20Sopenharmony_ci		if (ret)
1158c2ecf20Sopenharmony_ci			return ret;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
1198c2ecf20Sopenharmony_ci	    if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN]) {
1208c2ecf20Sopenharmony_ci		regfield = if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN];
1218c2ecf20Sopenharmony_ci		ret = regmap_field_write(regfield,
1228c2ecf20Sopenharmony_ci					 if_phy->rmii_clock_external);
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const
1298c2ecf20Sopenharmony_cistruct reg_field phy_gmii_sel_fields_am33xx[][PHY_GMII_SEL_LAST] = {
1308c2ecf20Sopenharmony_ci	{
1318c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 0, 1),
1328c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 4, 4),
1338c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 6, 6),
1348c2ecf20Sopenharmony_ci	},
1358c2ecf20Sopenharmony_ci	{
1368c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 2, 3),
1378c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 5, 5),
1388c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 7, 7),
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic const
1438c2ecf20Sopenharmony_cistruct phy_gmii_sel_soc_data phy_gmii_sel_soc_am33xx = {
1448c2ecf20Sopenharmony_ci	.num_ports = 2,
1458c2ecf20Sopenharmony_ci	.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE) |
1468c2ecf20Sopenharmony_ci		    BIT(PHY_GMII_SEL_RMII_IO_CLK_EN),
1478c2ecf20Sopenharmony_ci	.regfields = phy_gmii_sel_fields_am33xx,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const
1518c2ecf20Sopenharmony_cistruct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
1528c2ecf20Sopenharmony_ci	{
1538c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
1548c2ecf20Sopenharmony_ci	},
1558c2ecf20Sopenharmony_ci	{
1568c2ecf20Sopenharmony_ci		[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
1578c2ecf20Sopenharmony_ci	},
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const
1618c2ecf20Sopenharmony_cistruct phy_gmii_sel_soc_data phy_gmii_sel_soc_dra7 = {
1628c2ecf20Sopenharmony_ci	.num_ports = 2,
1638c2ecf20Sopenharmony_ci	.regfields = phy_gmii_sel_fields_dra7,
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic const
1678c2ecf20Sopenharmony_cistruct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
1688c2ecf20Sopenharmony_ci	.num_ports = 2,
1698c2ecf20Sopenharmony_ci	.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE),
1708c2ecf20Sopenharmony_ci	.regfields = phy_gmii_sel_fields_am33xx,
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic const
1748c2ecf20Sopenharmony_cistruct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = {
1758c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x0, 0, 2), },
1768c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4, 0, 2), },
1778c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x8, 0, 2), },
1788c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0xC, 0, 2), },
1798c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x10, 0, 2), },
1808c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x14, 0, 2), },
1818c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x18, 0, 2), },
1828c2ecf20Sopenharmony_ci	{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x1C, 0, 2), },
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic const
1868c2ecf20Sopenharmony_cistruct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
1878c2ecf20Sopenharmony_ci	.use_of_data = true,
1888c2ecf20Sopenharmony_ci	.regfields = phy_gmii_sel_fields_am654,
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic const struct of_device_id phy_gmii_sel_id_table[] = {
1928c2ecf20Sopenharmony_ci	{
1938c2ecf20Sopenharmony_ci		.compatible	= "ti,am3352-phy-gmii-sel",
1948c2ecf20Sopenharmony_ci		.data		= &phy_gmii_sel_soc_am33xx,
1958c2ecf20Sopenharmony_ci	},
1968c2ecf20Sopenharmony_ci	{
1978c2ecf20Sopenharmony_ci		.compatible	= "ti,dra7xx-phy-gmii-sel",
1988c2ecf20Sopenharmony_ci		.data		= &phy_gmii_sel_soc_dra7,
1998c2ecf20Sopenharmony_ci	},
2008c2ecf20Sopenharmony_ci	{
2018c2ecf20Sopenharmony_ci		.compatible	= "ti,am43xx-phy-gmii-sel",
2028c2ecf20Sopenharmony_ci		.data		= &phy_gmii_sel_soc_am33xx,
2038c2ecf20Sopenharmony_ci	},
2048c2ecf20Sopenharmony_ci	{
2058c2ecf20Sopenharmony_ci		.compatible	= "ti,dm814-phy-gmii-sel",
2068c2ecf20Sopenharmony_ci		.data		= &phy_gmii_sel_soc_dm814,
2078c2ecf20Sopenharmony_ci	},
2088c2ecf20Sopenharmony_ci	{
2098c2ecf20Sopenharmony_ci		.compatible	= "ti,am654-phy-gmii-sel",
2108c2ecf20Sopenharmony_ci		.data		= &phy_gmii_sel_soc_am654,
2118c2ecf20Sopenharmony_ci	},
2128c2ecf20Sopenharmony_ci	{}
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic const struct phy_ops phy_gmii_sel_ops = {
2178c2ecf20Sopenharmony_ci	.set_mode	= phy_gmii_sel_mode,
2188c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic struct phy *phy_gmii_sel_of_xlate(struct device *dev,
2228c2ecf20Sopenharmony_ci					 struct of_phandle_args *args)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct phy_gmii_sel_priv *priv = dev_get_drvdata(dev);
2258c2ecf20Sopenharmony_ci	int phy_id = args->args[0];
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (args->args_count < 1)
2288c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2298c2ecf20Sopenharmony_ci	if (!priv || !priv->if_phys)
2308c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
2318c2ecf20Sopenharmony_ci	if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
2328c2ecf20Sopenharmony_ci	    args->args_count < 2)
2338c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2348c2ecf20Sopenharmony_ci	if (phy_id > priv->num_ports)
2358c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2368c2ecf20Sopenharmony_ci	if (phy_id != priv->if_phys[phy_id - 1].id)
2378c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	phy_id--;
2408c2ecf20Sopenharmony_ci	if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN))
2418c2ecf20Sopenharmony_ci		priv->if_phys[phy_id].rmii_clock_external = args->args[1];
2428c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s id:%u ext:%d\n", __func__,
2438c2ecf20Sopenharmony_ci		priv->if_phys[phy_id].id, args->args[1]);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return priv->if_phys[phy_id].if_phy;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int phy_gmii_init_phy(struct phy_gmii_sel_priv *priv, int port,
2498c2ecf20Sopenharmony_ci			     struct phy_gmii_sel_phy_priv *if_phy)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
2528c2ecf20Sopenharmony_ci	struct device *dev = priv->dev;
2538c2ecf20Sopenharmony_ci	const struct reg_field *fields;
2548c2ecf20Sopenharmony_ci	struct regmap_field *regfield;
2558c2ecf20Sopenharmony_ci	struct reg_field field;
2568c2ecf20Sopenharmony_ci	int ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if_phy->id = port;
2598c2ecf20Sopenharmony_ci	if_phy->priv = priv;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	fields = soc_data->regfields[port - 1];
2628c2ecf20Sopenharmony_ci	field = *fields++;
2638c2ecf20Sopenharmony_ci	field.reg += priv->reg_offset;
2648c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s field %x %d %d\n", __func__,
2658c2ecf20Sopenharmony_ci		field.reg, field.msb, field.lsb);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	regfield = devm_regmap_field_alloc(dev, priv->regmap, field);
2688c2ecf20Sopenharmony_ci	if (IS_ERR(regfield))
2698c2ecf20Sopenharmony_ci		return PTR_ERR(regfield);
2708c2ecf20Sopenharmony_ci	if_phy->fields[PHY_GMII_SEL_PORT_MODE] = regfield;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	field = *fields++;
2738c2ecf20Sopenharmony_ci	field.reg += priv->reg_offset;
2748c2ecf20Sopenharmony_ci	if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE)) {
2758c2ecf20Sopenharmony_ci		regfield = devm_regmap_field_alloc(dev,
2768c2ecf20Sopenharmony_ci						   priv->regmap,
2778c2ecf20Sopenharmony_ci						   field);
2788c2ecf20Sopenharmony_ci		if (IS_ERR(regfield))
2798c2ecf20Sopenharmony_ci			return PTR_ERR(regfield);
2808c2ecf20Sopenharmony_ci		if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE] = regfield;
2818c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s field %x %d %d\n", __func__,
2828c2ecf20Sopenharmony_ci			field.reg, field.msb, field.lsb);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	field = *fields;
2868c2ecf20Sopenharmony_ci	field.reg += priv->reg_offset;
2878c2ecf20Sopenharmony_ci	if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) {
2888c2ecf20Sopenharmony_ci		regfield = devm_regmap_field_alloc(dev,
2898c2ecf20Sopenharmony_ci						   priv->regmap,
2908c2ecf20Sopenharmony_ci						   field);
2918c2ecf20Sopenharmony_ci		if (IS_ERR(regfield))
2928c2ecf20Sopenharmony_ci			return PTR_ERR(regfield);
2938c2ecf20Sopenharmony_ci		if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = regfield;
2948c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s field %x %d %d\n", __func__,
2958c2ecf20Sopenharmony_ci			field.reg, field.msb, field.lsb);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if_phy->if_phy = devm_phy_create(dev,
2998c2ecf20Sopenharmony_ci					 priv->dev->of_node,
3008c2ecf20Sopenharmony_ci					 &phy_gmii_sel_ops);
3018c2ecf20Sopenharmony_ci	if (IS_ERR(if_phy->if_phy)) {
3028c2ecf20Sopenharmony_ci		ret = PTR_ERR(if_phy->if_phy);
3038c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to create phy%d %d\n", port, ret);
3048c2ecf20Sopenharmony_ci		return ret;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci	phy_set_drvdata(if_phy->if_phy, if_phy);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
3148c2ecf20Sopenharmony_ci	struct phy_gmii_sel_phy_priv *if_phys;
3158c2ecf20Sopenharmony_ci	struct device *dev = priv->dev;
3168c2ecf20Sopenharmony_ci	int i, ret;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (soc_data->use_of_data) {
3198c2ecf20Sopenharmony_ci		const __be32 *offset;
3208c2ecf20Sopenharmony_ci		u64 size;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		offset = of_get_address(dev->of_node, 0, &size, NULL);
3238c2ecf20Sopenharmony_ci		if (!offset)
3248c2ecf20Sopenharmony_ci			return -EINVAL;
3258c2ecf20Sopenharmony_ci		priv->num_ports = size / sizeof(u32);
3268c2ecf20Sopenharmony_ci		if (!priv->num_ports)
3278c2ecf20Sopenharmony_ci			return -EINVAL;
3288c2ecf20Sopenharmony_ci		priv->reg_offset = __be32_to_cpu(*offset);
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if_phys = devm_kcalloc(dev, priv->num_ports,
3328c2ecf20Sopenharmony_ci			       sizeof(*if_phys), GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!if_phys)
3348c2ecf20Sopenharmony_ci		return -ENOMEM;
3358c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s %d\n", __func__, priv->num_ports);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_ports; i++) {
3388c2ecf20Sopenharmony_ci		ret = phy_gmii_init_phy(priv, i + 1, &if_phys[i]);
3398c2ecf20Sopenharmony_ci		if (ret)
3408c2ecf20Sopenharmony_ci			return ret;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	priv->if_phys = if_phys;
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int phy_gmii_sel_probe(struct platform_device *pdev)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3508c2ecf20Sopenharmony_ci	struct device_node *node = dev->of_node;
3518c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
3528c2ecf20Sopenharmony_ci	struct phy_gmii_sel_priv *priv;
3538c2ecf20Sopenharmony_ci	int ret;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node);
3568c2ecf20Sopenharmony_ci	if (!of_id)
3578c2ecf20Sopenharmony_ci		return -EINVAL;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
3608c2ecf20Sopenharmony_ci	if (!priv)
3618c2ecf20Sopenharmony_ci		return -ENOMEM;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	priv->dev = &pdev->dev;
3648c2ecf20Sopenharmony_ci	priv->soc_data = of_id->data;
3658c2ecf20Sopenharmony_ci	priv->num_ports = priv->soc_data->num_ports;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	priv->regmap = syscon_node_to_regmap(node->parent);
3688c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
3698c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->regmap);
3708c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get syscon %d\n", ret);
3718c2ecf20Sopenharmony_ci		return ret;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = phy_gmii_sel_init_ports(priv);
3758c2ecf20Sopenharmony_ci	if (ret)
3768c2ecf20Sopenharmony_ci		return ret;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	priv->phy_provider =
3818c2ecf20Sopenharmony_ci		devm_of_phy_provider_register(dev,
3828c2ecf20Sopenharmony_ci					      phy_gmii_sel_of_xlate);
3838c2ecf20Sopenharmony_ci	if (IS_ERR(priv->phy_provider)) {
3848c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->phy_provider);
3858c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to create phy provider %d\n", ret);
3868c2ecf20Sopenharmony_ci		return ret;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic struct platform_driver phy_gmii_sel_driver = {
3938c2ecf20Sopenharmony_ci	.probe		= phy_gmii_sel_probe,
3948c2ecf20Sopenharmony_ci	.driver		= {
3958c2ecf20Sopenharmony_ci		.name	= "phy-gmii-sel",
3968c2ecf20Sopenharmony_ci		.of_match_table = phy_gmii_sel_id_table,
3978c2ecf20Sopenharmony_ci	},
3988c2ecf20Sopenharmony_ci};
3998c2ecf20Sopenharmony_cimodule_platform_driver(phy_gmii_sel_driver);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
4038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI CPSW Port's PHY Interface Mode selection Driver");
404