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