brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 4edb796 Raw
243 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Texas Instruments Ethernet Switch Driver3 *4 * Copyright (C) 2013 Texas Instruments5 *6 * Module Author: Mugunthan V N <mugunthanvnm@ti.com>7 *8 */9 10#include <linux/platform_device.h>11#include <linux/init.h>12#include <linux/netdevice.h>13#include <linux/phy.h>14#include <linux/of.h>15 16#include "cpsw.h"17 18/* AM33xx SoC specific definitions for the CONTROL port */19#define AM33XX_GMII_SEL_MODE_MII	020#define AM33XX_GMII_SEL_MODE_RMII	121#define AM33XX_GMII_SEL_MODE_RGMII	222 23#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN	BIT(7)24#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN	BIT(6)25#define AM33XX_GMII_SEL_RGMII2_IDMODE	BIT(5)26#define AM33XX_GMII_SEL_RGMII1_IDMODE	BIT(4)27 28#define GMII_SEL_MODE_MASK		0x329 30struct cpsw_phy_sel_priv {31	struct device	*dev;32	u32 __iomem	*gmii_sel;33	bool		rmii_clock_external;34	void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,35			     phy_interface_t phy_mode, int slave);36};37 38 39static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,40				 phy_interface_t phy_mode, int slave)41{42	u32 reg;43	u32 mask;44	u32 mode = 0;45	bool rgmii_id = false;46 47	reg = readl(priv->gmii_sel);48 49	switch (phy_mode) {50	case PHY_INTERFACE_MODE_RMII:51		mode = AM33XX_GMII_SEL_MODE_RMII;52		break;53 54	case PHY_INTERFACE_MODE_RGMII:55		mode = AM33XX_GMII_SEL_MODE_RGMII;56		break;57 58	case PHY_INTERFACE_MODE_RGMII_ID:59	case PHY_INTERFACE_MODE_RGMII_RXID:60	case PHY_INTERFACE_MODE_RGMII_TXID:61		mode = AM33XX_GMII_SEL_MODE_RGMII;62		rgmii_id = true;63		break;64 65	default:66		dev_warn(priv->dev,67			 "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",68			phy_modes(phy_mode));69		fallthrough;70	case PHY_INTERFACE_MODE_MII:71		mode = AM33XX_GMII_SEL_MODE_MII;72		break;73	}74 75	mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);76	mask |= BIT(slave + 4);77	mode <<= slave * 2;78 79	if (priv->rmii_clock_external) {80		if (slave == 0)81			mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;82		else83			mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;84	}85 86	if (rgmii_id) {87		if (slave == 0)88			mode |= AM33XX_GMII_SEL_RGMII1_IDMODE;89		else90			mode |= AM33XX_GMII_SEL_RGMII2_IDMODE;91	}92 93	reg &= ~mask;94	reg |= mode;95 96	writel(reg, priv->gmii_sel);97}98 99static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,100				 phy_interface_t phy_mode, int slave)101{102	u32 reg;103	u32 mask;104	u32 mode = 0;105 106	reg = readl(priv->gmii_sel);107 108	switch (phy_mode) {109	case PHY_INTERFACE_MODE_RMII:110		mode = AM33XX_GMII_SEL_MODE_RMII;111		break;112 113	case PHY_INTERFACE_MODE_RGMII:114	case PHY_INTERFACE_MODE_RGMII_ID:115	case PHY_INTERFACE_MODE_RGMII_RXID:116	case PHY_INTERFACE_MODE_RGMII_TXID:117		mode = AM33XX_GMII_SEL_MODE_RGMII;118		break;119 120	default:121		dev_warn(priv->dev,122			 "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",123			phy_modes(phy_mode));124		fallthrough;125	case PHY_INTERFACE_MODE_MII:126		mode = AM33XX_GMII_SEL_MODE_MII;127		break;128	}129 130	switch (slave) {131	case 0:132		mask = GMII_SEL_MODE_MASK;133		break;134	case 1:135		mask = GMII_SEL_MODE_MASK << 4;136		mode <<= 4;137		break;138	default:139		dev_err(priv->dev, "invalid slave number...\n");140		return;141	}142 143	if (priv->rmii_clock_external)144		dev_err(priv->dev, "RMII External clock is not supported\n");145 146	reg &= ~mask;147	reg |= mode;148 149	writel(reg, priv->gmii_sel);150}151 152static struct platform_driver cpsw_phy_sel_driver;153static int match(struct device *dev, const void *data)154{155	const struct device_node *node = (const struct device_node *)data;156	return dev->of_node == node &&157		dev->driver == &cpsw_phy_sel_driver.driver;158}159 160void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)161{162	struct device_node *node;163	struct cpsw_phy_sel_priv *priv;164 165	node = of_parse_phandle(dev->of_node, "cpsw-phy-sel", 0);166	if (!node) {167		node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");168		if (!node) {169			dev_err(dev, "Phy mode driver DT not found\n");170			return;171		}172	}173 174	dev = bus_find_device(&platform_bus_type, NULL, node, match);175	if (!dev) {176		dev_err(dev, "unable to find platform device for %pOF\n", node);177		goto out;178	}179 180	priv = dev_get_drvdata(dev);181 182	priv->cpsw_phy_sel(priv, phy_mode, slave);183 184	put_device(dev);185out:186	of_node_put(node);187}188EXPORT_SYMBOL_GPL(cpsw_phy_sel);189 190static const struct of_device_id cpsw_phy_sel_id_table[] = {191	{192		.compatible	= "ti,am3352-cpsw-phy-sel",193		.data		= &cpsw_gmii_sel_am3352,194	},195	{196		.compatible	= "ti,dra7xx-cpsw-phy-sel",197		.data		= &cpsw_gmii_sel_dra7xx,198	},199	{200		.compatible	= "ti,am43xx-cpsw-phy-sel",201		.data		= &cpsw_gmii_sel_am3352,202	},203	{}204};205 206static int cpsw_phy_sel_probe(struct platform_device *pdev)207{208	const struct of_device_id *of_id;209	struct cpsw_phy_sel_priv *priv;210 211	of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);212	if (!of_id)213		return -EINVAL;214 215	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);216	if (!priv) {217		dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");218		return -ENOMEM;219	}220 221	priv->dev = &pdev->dev;222	priv->cpsw_phy_sel = of_id->data;223 224	priv->gmii_sel = devm_platform_ioremap_resource_byname(pdev, "gmii-sel");225	if (IS_ERR(priv->gmii_sel))226		return PTR_ERR(priv->gmii_sel);227 228	priv->rmii_clock_external = of_property_read_bool(pdev->dev.of_node, "rmii-clock-ext");229 230	dev_set_drvdata(&pdev->dev, priv);231 232	return 0;233}234 235static struct platform_driver cpsw_phy_sel_driver = {236	.probe		= cpsw_phy_sel_probe,237	.driver		= {238		.name	= "cpsw-phy-sel",239		.of_match_table = cpsw_phy_sel_id_table,240	},241};242builtin_platform_driver(cpsw_phy_sel_driver);243