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 mode <<= slave * 2; 85 86 if (priv->rmii_clock_external) { 87 if (slave == 0) 88 mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; 89 else 90 mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN; 91 } 92 93 if (rgmii_id) { 94 if (slave == 0) 95 mode |= AM33XX_GMII_SEL_RGMII1_IDMODE; 96 else 97 mode |= AM33XX_GMII_SEL_RGMII2_IDMODE; 98 } 99 100 reg &= ~mask; 101 reg |= mode; 102 103 writel(reg, priv->gmii_sel); 104 } 105 106 static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv, 107 phy_interface_t phy_mode, int slave) 108 { 109 u32 reg; 110 u32 mask; 111 u32 mode = 0; 112 113 reg = readl(priv->gmii_sel); 114 115 switch (phy_mode) { 116 case PHY_INTERFACE_MODE_RMII: 117 mode = AM33XX_GMII_SEL_MODE_RMII; 118 break; 119 120 case PHY_INTERFACE_MODE_RGMII: 121 case PHY_INTERFACE_MODE_RGMII_ID: 122 case PHY_INTERFACE_MODE_RGMII_RXID: 123 case PHY_INTERFACE_MODE_RGMII_TXID: 124 mode = AM33XX_GMII_SEL_MODE_RGMII; 125 break; 126 127 default: 128 dev_warn(priv->dev, 129 "Unsupported PHY mode: \"%s\". Defaulting to MII.\n", 130 phy_modes(phy_mode)); 131 /* fallthrough */ 132 case PHY_INTERFACE_MODE_MII: 133 mode = AM33XX_GMII_SEL_MODE_MII; 134 break; 135 }; 136 137 switch (slave) { 138 case 0: 139 mask = GMII_SEL_MODE_MASK; 140 break; 141 case 1: 142 mask = GMII_SEL_MODE_MASK << 4; 143 mode <<= 4; 144 break; 145 default: 146 dev_err(priv->dev, "invalid slave number...\n"); 147 return; 148 } 149 150 if (priv->rmii_clock_external) 151 dev_err(priv->dev, "RMII External clock is not supported\n"); 152 153 reg &= ~mask; 154 reg |= mode; 155 156 writel(reg, priv->gmii_sel); 157 } 158 159 static struct platform_driver cpsw_phy_sel_driver; 160 static int match(struct device *dev, void *data) 161 { 162 struct device_node *node = (struct device_node *)data; 163 return dev->of_node == node && 164 dev->driver == &cpsw_phy_sel_driver.driver; 165 } 166 167 void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave) 168 { 169 struct device_node *node; 170 struct cpsw_phy_sel_priv *priv; 171 172 node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel"); 173 if (!node) { 174 dev_err(dev, "Phy mode driver DT not found\n"); 175 return; 176 } 177 178 dev = bus_find_device(&platform_bus_type, NULL, node, match); 179 priv = dev_get_drvdata(dev); 180 181 priv->cpsw_phy_sel(priv, phy_mode, slave); 182 } 183 EXPORT_SYMBOL_GPL(cpsw_phy_sel); 184 185 static const struct of_device_id cpsw_phy_sel_id_table[] = { 186 { 187 .compatible = "ti,am3352-cpsw-phy-sel", 188 .data = &cpsw_gmii_sel_am3352, 189 }, 190 { 191 .compatible = "ti,dra7xx-cpsw-phy-sel", 192 .data = &cpsw_gmii_sel_dra7xx, 193 }, 194 { 195 .compatible = "ti,am43xx-cpsw-phy-sel", 196 .data = &cpsw_gmii_sel_am3352, 197 }, 198 {} 199 }; 200 201 static int cpsw_phy_sel_probe(struct platform_device *pdev) 202 { 203 struct resource *res; 204 const struct of_device_id *of_id; 205 struct cpsw_phy_sel_priv *priv; 206 207 of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node); 208 if (!of_id) 209 return -EINVAL; 210 211 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 212 if (!priv) { 213 dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n"); 214 return -ENOMEM; 215 } 216 217 priv->dev = &pdev->dev; 218 priv->cpsw_phy_sel = of_id->data; 219 220 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel"); 221 priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res); 222 if (IS_ERR(priv->gmii_sel)) 223 return PTR_ERR(priv->gmii_sel); 224 225 if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL)) 226 priv->rmii_clock_external = true; 227 228 dev_set_drvdata(&pdev->dev, priv); 229 230 return 0; 231 } 232 233 static struct platform_driver cpsw_phy_sel_driver = { 234 .probe = cpsw_phy_sel_probe, 235 .driver = { 236 .name = "cpsw-phy-sel", 237 .of_match_table = cpsw_phy_sel_id_table, 238 }, 239 }; 240 builtin_platform_driver(cpsw_phy_sel_driver); 241