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 31 struct 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 40 static 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 100 static 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 153 static struct platform_driver cpsw_phy_sel_driver; 154 static 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 161 void 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); 186 out: 187 of_node_put(node); 188 } 189 EXPORT_SYMBOL_GPL(cpsw_phy_sel); 190 191 static 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 207 static int cpsw_phy_sel_probe(struct platform_device *pdev) 208 { 209 struct resource *res; 210 const struct of_device_id *of_id; 211 struct cpsw_phy_sel_priv *priv; 212 213 of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node); 214 if (!of_id) 215 return -EINVAL; 216 217 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 218 if (!priv) { 219 dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n"); 220 return -ENOMEM; 221 } 222 223 priv->dev = &pdev->dev; 224 priv->cpsw_phy_sel = of_id->data; 225 226 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel"); 227 priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res); 228 if (IS_ERR(priv->gmii_sel)) 229 return PTR_ERR(priv->gmii_sel); 230 231 if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL)) 232 priv->rmii_clock_external = true; 233 234 dev_set_drvdata(&pdev->dev, priv); 235 236 return 0; 237 } 238 239 static struct platform_driver cpsw_phy_sel_driver = { 240 .probe = cpsw_phy_sel_probe, 241 .driver = { 242 .name = "cpsw-phy-sel", 243 .of_match_table = cpsw_phy_sel_id_table, 244 }, 245 }; 246 builtin_platform_driver(cpsw_phy_sel_driver); 247