linux/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
<<
>>
Prefs
   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
  32static 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
  38        if (!np)
  39                return -ENODEV;
  40
  41        *mac = of_get_mac_address(np);
  42        plat->interface = of_get_phy_mode(np);
  43        plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
  44                                           sizeof(struct stmmac_mdio_bus_data),
  45                                           GFP_KERNEL);
  46
  47        /*
  48         * Currently only the properties needed on SPEAr600
  49         * are provided. All other properties should be added
  50         * once needed on other platforms.
  51         */
  52        if (of_device_is_compatible(np, "st,spear600-gmac") ||
  53                of_device_is_compatible(np, "snps,dwmac-3.70a") ||
  54                of_device_is_compatible(np, "snps,dwmac")) {
  55                plat->has_gmac = 1;
  56                plat->pmt = 1;
  57        }
  58
  59        return 0;
  60}
  61#else
  62static int stmmac_probe_config_dt(struct platform_device *pdev,
  63                                  struct plat_stmmacenet_data *plat,
  64                                  const char **mac)
  65{
  66        return -ENOSYS;
  67}
  68#endif /* CONFIG_OF */
  69
  70/**
  71 * stmmac_pltfr_probe
  72 * @pdev: platform device pointer
  73 * Description: platform_device probe function. It allocates
  74 * the necessary resources and invokes the main to init
  75 * the net device, register the mdio bus etc.
  76 */
  77static int stmmac_pltfr_probe(struct platform_device *pdev)
  78{
  79        int ret = 0;
  80        struct resource *res;
  81        struct device *dev = &pdev->dev;
  82        void __iomem *addr = NULL;
  83        struct stmmac_priv *priv = NULL;
  84        struct plat_stmmacenet_data *plat_dat = NULL;
  85        const char *mac = NULL;
  86
  87        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  88        if (!res)
  89                return -ENODEV;
  90
  91        addr = devm_ioremap_resource(dev, res);
  92        if (IS_ERR(addr))
  93                return PTR_ERR(addr);
  94
  95        if (pdev->dev.of_node) {
  96                plat_dat = devm_kzalloc(&pdev->dev,
  97                                        sizeof(struct plat_stmmacenet_data),
  98                                        GFP_KERNEL);
  99                if (!plat_dat) {
 100                        pr_err("%s: ERROR: no memory", __func__);
 101                        return  -ENOMEM;
 102                }
 103
 104                ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
 105                if (ret) {
 106                        pr_err("%s: main dt probe failed", __func__);
 107                        return ret;
 108                }
 109        } else {
 110                plat_dat = pdev->dev.platform_data;
 111        }
 112
 113        /* Custom initialisation (if needed)*/
 114        if (plat_dat->init) {
 115                ret = plat_dat->init(pdev);
 116                if (unlikely(ret))
 117                        return ret;
 118        }
 119
 120        priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
 121        if (!priv) {
 122                pr_err("%s: main driver probe failed", __func__);
 123                return -ENODEV;
 124        }
 125
 126        /* Get MAC address if available (DT) */
 127        if (mac)
 128                memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
 129
 130        /* Get the MAC information */
 131        priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
 132        if (priv->dev->irq == -ENXIO) {
 133                pr_err("%s: ERROR: MAC IRQ configuration "
 134                       "information not found\n", __func__);
 135                return -ENXIO;
 136        }
 137
 138        /*
 139         * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
 140         * The external wake up irq can be passed through the platform code
 141         * named as "eth_wake_irq"
 142         *
 143         * In case the wake up interrupt is not passed from the platform
 144         * so the driver will continue to use the mac irq (ndev->irq)
 145         */
 146        priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
 147        if (priv->wol_irq == -ENXIO)
 148                priv->wol_irq = priv->dev->irq;
 149
 150        priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
 151
 152        platform_set_drvdata(pdev, priv->dev);
 153
 154        pr_debug("STMMAC platform driver registration completed");
 155
 156        return 0;
 157}
 158
 159/**
 160 * stmmac_pltfr_remove
 161 * @pdev: platform device pointer
 162 * Description: this function calls the main to free the net resources
 163 * and calls the platforms hook and release the resources (e.g. mem).
 164 */
 165static int stmmac_pltfr_remove(struct platform_device *pdev)
 166{
 167        struct net_device *ndev = platform_get_drvdata(pdev);
 168        struct stmmac_priv *priv = netdev_priv(ndev);
 169        int ret = stmmac_dvr_remove(ndev);
 170
 171        if (priv->plat->exit)
 172                priv->plat->exit(pdev);
 173
 174        platform_set_drvdata(pdev, NULL);
 175
 176        return ret;
 177}
 178
 179#ifdef CONFIG_PM
 180static int stmmac_pltfr_suspend(struct device *dev)
 181{
 182        struct net_device *ndev = dev_get_drvdata(dev);
 183
 184        return stmmac_suspend(ndev);
 185}
 186
 187static int stmmac_pltfr_resume(struct device *dev)
 188{
 189        struct net_device *ndev = dev_get_drvdata(dev);
 190
 191        return stmmac_resume(ndev);
 192}
 193
 194int stmmac_pltfr_freeze(struct device *dev)
 195{
 196        int ret;
 197        struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev);
 198        struct net_device *ndev = dev_get_drvdata(dev);
 199        struct platform_device *pdev = to_platform_device(dev);
 200
 201        ret = stmmac_freeze(ndev);
 202        if (plat_dat->exit)
 203                plat_dat->exit(pdev);
 204
 205        return ret;
 206}
 207
 208int stmmac_pltfr_restore(struct device *dev)
 209{
 210        struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev);
 211        struct net_device *ndev = dev_get_drvdata(dev);
 212        struct platform_device *pdev = to_platform_device(dev);
 213
 214        if (plat_dat->init)
 215                plat_dat->init(pdev);
 216
 217        return stmmac_restore(ndev);
 218}
 219
 220static const struct dev_pm_ops stmmac_pltfr_pm_ops = {
 221        .suspend = stmmac_pltfr_suspend,
 222        .resume = stmmac_pltfr_resume,
 223        .freeze = stmmac_pltfr_freeze,
 224        .thaw = stmmac_pltfr_restore,
 225        .restore = stmmac_pltfr_restore,
 226};
 227#else
 228static const struct dev_pm_ops stmmac_pltfr_pm_ops;
 229#endif /* CONFIG_PM */
 230
 231static const struct of_device_id stmmac_dt_ids[] = {
 232        { .compatible = "st,spear600-gmac"},
 233        { .compatible = "snps,dwmac-3.70a"},
 234        { .compatible = "snps,dwmac"},
 235        { /* sentinel */ }
 236};
 237MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
 238
 239struct platform_driver stmmac_pltfr_driver = {
 240        .probe = stmmac_pltfr_probe,
 241        .remove = stmmac_pltfr_remove,
 242        .driver = {
 243                   .name = STMMAC_RESOURCE_NAME,
 244                   .owner = THIS_MODULE,
 245                   .pm = &stmmac_pltfr_pm_ops,
 246                   .of_match_table = of_match_ptr(stmmac_dt_ids),
 247                   },
 248};
 249
 250MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver");
 251MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
 252MODULE_LICENSE("GPL");
 253