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