1 /* Texas Instruments Ethernet Switch Driver 2 * 3 * Copyright (C) 2013 Texas Instruments 4 * 5 * Module Author: Mugunthan V N <mugunthanvnm@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 * 11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 12 * kind, whether express or implied; without even the implied warranty 13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/platform_device.h> 18 #include <linux/init.h> 19 #include <linux/netdevice.h> 20 #include <linux/phy.h> 21 #include <linux/of.h> 22 #include <linux/of_device.h> 23 24 #include "cpsw.h" 25 26 /* AM33xx SoC specific definitions for the CONTROL port */ 27 #define AM33XX_GMII_SEL_MODE_MII 0 28 #define AM33XX_GMII_SEL_MODE_RMII 1 29 #define AM33XX_GMII_SEL_MODE_RGMII 2 30 31 #define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7) 32 #define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6) 33 #define AM33XX_GMII_SEL_RGMII2_IDMODE BIT(5) 34 #define AM33XX_GMII_SEL_RGMII1_IDMODE BIT(4) 35 36 #define GMII_SEL_MODE_MASK 0x3 37 38 struct cpsw_phy_sel_priv { 39 struct device *dev; 40 u32 __iomem *gmii_sel; 41 bool rmii_clock_external; 42 void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv, 43 phy_interface_t phy_mode, int slave); 44 }; 45 46 47 static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv, 48 phy_interface_t phy_mode, int slave) 49 { 50 u32 reg; 51 u32 mask; 52 u32 mode = 0; 53 bool rgmii_id = false; 54 55 reg = readl(priv->gmii_sel); 56 57 switch (phy_mode) { 58 case PHY_INTERFACE_MODE_RMII: 59 mode = AM33XX_GMII_SEL_MODE_RMII; 60 break; 61 62 case PHY_INTERFACE_MODE_RGMII: 63 mode = AM33XX_GMII_SEL_MODE_RGMII; 64 break; 65 66 case PHY_INTERFACE_MODE_RGMII_ID: 67 case PHY_INTERFACE_MODE_RGMII_RXID: 68 case PHY_INTERFACE_MODE_RGMII_TXID: 69 mode = AM33XX_GMII_SEL_MODE_RGMII; 70 rgmii_id = true; 71 break; 72 73 default: 74 dev_warn(priv->dev, 75 "Unsupported PHY mode: \"%s\". Defaulting to MII.\n", 76 phy_modes(phy_mode)); 77 /* fallthrough */ 78 case PHY_INTERFACE_MODE_MII: 79 mode = AM33XX_GMII_SEL_MODE_MII; 80 break; 81 }; 82 83 mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6); 84 mask |= BIT(slave + 4); 85 mode <<= slave * 2; 86 87 if (priv->rmii_clock_external) { 88 if (slave == 0) 89 mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; 90 else 91 mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN; 92 } 93 94 if (rgmii_id) { 95 if (slave == 0) 96 mode |= AM33XX_GMII_SEL_RGMII1_IDMODE; 97 else 98 mode |= AM33XX_GMII_SEL_RGMII2_IDMODE; 99 } 100 101 reg &= ~mask; 102 reg |= mode; 103 104 writel(reg, priv->gmii_sel); 105 } 106 107 static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv, 108 phy_interface_t phy_mode, int slave) 109 { 110 u32 reg; 111 u32 mask; 112 u32 mode = 0; 113 114 reg = readl(priv->gmii_sel); 115 116 switch (phy_mode) { 117 case PHY_INTERFACE_MODE_RMII: 118 mode = AM33XX_GMII_SEL_MODE_RMII; 119 break; 120 121 case PHY_INTERFACE_MODE_RGMII: 122 case PHY_INTERFACE_MODE_RGMII_ID: 123 case PHY_INTERFACE_MODE_RGMII_RXID: 124 case PHY_INTERFACE_MODE_RGMII_TXID: 125 mode = AM33XX_GMII_SEL_MODE_RGMII; 126 break; 127 128 default: 129 dev_warn(priv->dev, 130 "Unsupported PHY mode: \"%s\". Defaulting to MII.\n", 131 phy_modes(phy_mode)); 132 /* fallthrough */ 133 case PHY_INTERFACE_MODE_MII: 134 mode = AM33XX_GMII_SEL_MODE_MII; 135 break; 136 }; 137 138 switch (slave) { 139 case 0: 140 mask = GMII_SEL_MODE_MASK; 141 break; 142 case 1: 143 mask = GMII_SEL_MODE_MASK << 4; 144 mode <<= 4; 145 break; 146 default: 147 dev_err(priv->dev, "invalid slave number...\n"); 148 return; 149 } 150 151 if (priv->rmii_clock_external) 152 dev_err(priv->dev, "RMII External clock is not supported\n"); 153 154 reg &= ~mask; 155 reg |= mode; 156 157 writel(reg, priv->gmii_sel); 158 } 159 160 static struct platform_driver cpsw_phy_sel_driver; 161 static int match(struct device *dev, void *data) 162 { 163 struct device_node *node = (struct device_node *)data; 164 return dev->of_node == node && 165 dev->driver == &cpsw_phy_sel_driver.driver; 166 } 167 168 void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave) 169 { 170 struct device_node *node; 171 struct cpsw_phy_sel_priv *priv; 172 173 node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel"); 174 if (!node) { 175 dev_err(dev, "Phy mode driver DT not found\n"); 176 return; 177 } 178 179 dev = bus_find_device(&platform_bus_type, NULL, node, match); 180 of_node_put(node); 181 priv = dev_get_drvdata(dev); 182 183 priv->cpsw_phy_sel(priv, phy_mode, slave); 184 185 put_device(dev); 186 } 187 EXPORT_SYMBOL_GPL(cpsw_phy_sel); 188 189 static const struct of_device_id cpsw_phy_sel_id_table[] = { 190 { 191 .compatible = "ti,am3352-cpsw-phy-sel", 192 .data = &cpsw_gmii_sel_am3352, 193 }, 194 { 195 .compatible = "ti,dra7xx-cpsw-phy-sel", 196 .data = &cpsw_gmii_sel_dra7xx, 197 }, 198 { 199 .compatible = "ti,am43xx-cpsw-phy-sel", 200 .data = &cpsw_gmii_sel_am3352, 201 }, 202 {} 203 }; 204 205 static int cpsw_phy_sel_probe(struct platform_device *pdev) 206 { 207 struct resource *res; 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 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel"); 225 priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res); 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 237 static 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 }; 244 builtin_platform_driver(cpsw_phy_sel_driver); 245