1 /******************************************************************************* 2 This contains the functions to handle the platform driver. 3 4 Copyright (C) 2007-2011 STMicroelectronics Ltd 5 6 This program is free software; you can redistribute it and/or modify it 7 under the terms and conditions of the GNU General Public License, 8 version 2, as published by the Free Software Foundation. 9 10 This program is distributed in the hope it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 more details. 14 15 You should have received a copy of the GNU General Public License along with 16 this program; if not, write to the Free Software Foundation, Inc., 17 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 18 19 The full GNU General Public License is included in this distribution in 20 the file called "COPYING". 21 22 Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 23 *******************************************************************************/ 24 25 #include <linux/platform_device.h> 26 #include <linux/io.h> 27 #include <linux/of.h> 28 #include <linux/of_net.h> 29 #include "stmmac.h" 30 31 #ifdef CONFIG_OF 32 static int stmmac_probe_config_dt(struct platform_device *pdev, 33 struct plat_stmmacenet_data *plat, 34 const char **mac) 35 { 36 struct device_node *np = pdev->dev.of_node; 37 struct stmmac_dma_cfg *dma_cfg; 38 39 if (!np) 40 return -ENODEV; 41 42 *mac = of_get_mac_address(np); 43 plat->interface = of_get_phy_mode(np); 44 45 plat->bus_id = of_alias_get_id(np, "ethernet"); 46 if (plat->bus_id < 0) 47 plat->bus_id = 0; 48 49 of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr); 50 51 plat->mdio_bus_data = devm_kzalloc(&pdev->dev, 52 sizeof(struct stmmac_mdio_bus_data), 53 GFP_KERNEL); 54 55 /* 56 * Currently only the properties needed on SPEAr600 57 * are provided. All other properties should be added 58 * once needed on other platforms. 59 */ 60 if (of_device_is_compatible(np, "st,spear600-gmac") || 61 of_device_is_compatible(np, "snps,dwmac-3.70a") || 62 of_device_is_compatible(np, "snps,dwmac")) { 63 plat->has_gmac = 1; 64 plat->pmt = 1; 65 } 66 67 if (of_device_is_compatible(np, "snps,dwmac-3.610") || 68 of_device_is_compatible(np, "snps,dwmac-3.710")) { 69 plat->enh_desc = 1; 70 plat->bugged_jumbo = 1; 71 plat->force_sf_dma_mode = 1; 72 } 73 74 dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL); 75 if (!dma_cfg) 76 return -ENOMEM; 77 78 plat->dma_cfg = dma_cfg; 79 of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); 80 dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); 81 dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); 82 83 return 0; 84 } 85 #else 86 static int stmmac_probe_config_dt(struct platform_device *pdev, 87 struct plat_stmmacenet_data *plat, 88 const char **mac) 89 { 90 return -ENOSYS; 91 } 92 #endif /* CONFIG_OF */ 93 94 /** 95 * stmmac_pltfr_probe 96 * @pdev: platform device pointer 97 * Description: platform_device probe function. It allocates 98 * the necessary resources and invokes the main to init 99 * the net device, register the mdio bus etc. 100 */ 101 static int stmmac_pltfr_probe(struct platform_device *pdev) 102 { 103 int ret = 0; 104 struct resource *res; 105 struct device *dev = &pdev->dev; 106 void __iomem *addr = NULL; 107 struct stmmac_priv *priv = NULL; 108 struct plat_stmmacenet_data *plat_dat = NULL; 109 const char *mac = NULL; 110 111 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112 if (!res) 113 return -ENODEV; 114 115 addr = devm_ioremap_resource(dev, res); 116 if (IS_ERR(addr)) 117 return PTR_ERR(addr); 118 119 plat_dat = pdev->dev.platform_data; 120 if (pdev->dev.of_node) { 121 if (!plat_dat) 122 plat_dat = devm_kzalloc(&pdev->dev, 123 sizeof(struct plat_stmmacenet_data), 124 GFP_KERNEL); 125 if (!plat_dat) { 126 pr_err("%s: ERROR: no memory", __func__); 127 return -ENOMEM; 128 } 129 130 ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); 131 if (ret) { 132 pr_err("%s: main dt probe failed", __func__); 133 return ret; 134 } 135 } 136 137 /* Custom initialisation (if needed)*/ 138 if (plat_dat->init) { 139 ret = plat_dat->init(pdev); 140 if (unlikely(ret)) 141 return ret; 142 } 143 144 priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); 145 if (!priv) { 146 pr_err("%s: main driver probe failed", __func__); 147 return -ENODEV; 148 } 149 150 /* Get MAC address if available (DT) */ 151 if (mac) 152 memcpy(priv->dev->dev_addr, mac, ETH_ALEN); 153 154 /* Get the MAC information */ 155 priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); 156 if (priv->dev->irq == -ENXIO) { 157 pr_err("%s: ERROR: MAC IRQ configuration " 158 "information not found\n", __func__); 159 return -ENXIO; 160 } 161 162 /* 163 * On some platforms e.g. SPEAr the wake up irq differs from the mac irq 164 * The external wake up irq can be passed through the platform code 165 * named as "eth_wake_irq" 166 * 167 * In case the wake up interrupt is not passed from the platform 168 * so the driver will continue to use the mac irq (ndev->irq) 169 */ 170 priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); 171 if (priv->wol_irq == -ENXIO) 172 priv->wol_irq = priv->dev->irq; 173 174 priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); 175 176 platform_set_drvdata(pdev, priv->dev); 177 178 pr_debug("STMMAC platform driver registration completed"); 179 180 return 0; 181 } 182 183 /** 184 * stmmac_pltfr_remove 185 * @pdev: platform device pointer 186 * Description: this function calls the main to free the net resources 187 * and calls the platforms hook and release the resources (e.g. mem). 188 */ 189 static int stmmac_pltfr_remove(struct platform_device *pdev) 190 { 191 struct net_device *ndev = platform_get_drvdata(pdev); 192 struct stmmac_priv *priv = netdev_priv(ndev); 193 int ret = stmmac_dvr_remove(ndev); 194 195 if (priv->plat->exit) 196 priv->plat->exit(pdev); 197 198 return ret; 199 } 200 201 #ifdef CONFIG_PM 202 static int stmmac_pltfr_suspend(struct device *dev) 203 { 204 struct net_device *ndev = dev_get_drvdata(dev); 205 206 return stmmac_suspend(ndev); 207 } 208 209 static int stmmac_pltfr_resume(struct device *dev) 210 { 211 struct net_device *ndev = dev_get_drvdata(dev); 212 213 return stmmac_resume(ndev); 214 } 215 216 int stmmac_pltfr_freeze(struct device *dev) 217 { 218 int ret; 219 struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev); 220 struct net_device *ndev = dev_get_drvdata(dev); 221 struct platform_device *pdev = to_platform_device(dev); 222 223 ret = stmmac_freeze(ndev); 224 if (plat_dat->exit) 225 plat_dat->exit(pdev); 226 227 return ret; 228 } 229 230 int stmmac_pltfr_restore(struct device *dev) 231 { 232 struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev); 233 struct net_device *ndev = dev_get_drvdata(dev); 234 struct platform_device *pdev = to_platform_device(dev); 235 236 if (plat_dat->init) 237 plat_dat->init(pdev); 238 239 return stmmac_restore(ndev); 240 } 241 242 static const struct dev_pm_ops stmmac_pltfr_pm_ops = { 243 .suspend = stmmac_pltfr_suspend, 244 .resume = stmmac_pltfr_resume, 245 .freeze = stmmac_pltfr_freeze, 246 .thaw = stmmac_pltfr_restore, 247 .restore = stmmac_pltfr_restore, 248 }; 249 #else 250 static const struct dev_pm_ops stmmac_pltfr_pm_ops; 251 #endif /* CONFIG_PM */ 252 253 static const struct of_device_id stmmac_dt_ids[] = { 254 { .compatible = "st,spear600-gmac"}, 255 { .compatible = "snps,dwmac-3.610"}, 256 { .compatible = "snps,dwmac-3.70a"}, 257 { .compatible = "snps,dwmac-3.710"}, 258 { .compatible = "snps,dwmac"}, 259 { /* sentinel */ } 260 }; 261 MODULE_DEVICE_TABLE(of, stmmac_dt_ids); 262 263 struct platform_driver stmmac_pltfr_driver = { 264 .probe = stmmac_pltfr_probe, 265 .remove = stmmac_pltfr_remove, 266 .driver = { 267 .name = STMMAC_RESOURCE_NAME, 268 .owner = THIS_MODULE, 269 .pm = &stmmac_pltfr_pm_ops, 270 .of_match_table = of_match_ptr(stmmac_dt_ids), 271 }, 272 }; 273 274 MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver"); 275 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 276 MODULE_LICENSE("GPL"); 277