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_request_and_ioremap(dev, res);
  92        if (!addr) {
  93                pr_err("%s: ERROR: memory mapping failed", __func__);
  94                return -ENOMEM;
  95        }
  96
  97        if (pdev->dev.of_node) {
  98                plat_dat = devm_kzalloc(&pdev->dev,
  99                                        sizeof(struct plat_stmmacenet_data),
 100                                        GFP_KERNEL);
 101                if (!plat_dat) {
 102                        pr_err("%s: ERROR: no memory", __func__);
 103                        return  -ENOMEM;
 104                }
 105
 106                ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
 107                if (ret) {
 108                        pr_err("%s: main dt probe failed", __func__);
 109                        return ret;
 110                }
 111        } else {
 112                plat_dat = pdev->dev.platform_data;
 113        }
 114
 115        /* Custom initialisation (if needed)*/
 116        if (plat_dat->init) {
 117                ret = plat_dat->init(pdev);
 118                if (unlikely(ret))
 119                        return ret;
 120        }
 121
 122        priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
 123        if (!priv) {
 124                pr_err("%s: main driver probe failed", __func__);
 125                return -ENODEV;
 126        }
 127
 128        /* Get MAC address if available (DT) */
 129        if (mac)
 130                memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
 131
 132        /* Get the MAC information */
 133        priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
 134        if (priv->dev->irq == -ENXIO) {
 135                pr_err("%s: ERROR: MAC IRQ configuration "
 136                       "information not found\n", __func__);
 137                return -ENXIO;
 138        }
 139
 140        /*
 141         * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
 142         * The external wake up irq can be passed through the platform code
 143         * named as "eth_wake_irq"
 144         *
 145         * In case the wake up interrupt is not passed from the platform
 146         * so the driver will continue to use the mac irq (ndev->irq)
 147         */
 148        priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
 149        if (priv->wol_irq == -ENXIO)
 150                priv->wol_irq = priv->dev->irq;
 151
 152        priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
 153
 154        platform_set_drvdata(pdev, priv->dev);
 155
 156        pr_debug("STMMAC platform driver registration completed");
 157
 158        return 0;
 159}
 160
 161/**
 162 * stmmac_pltfr_remove
 163 * @pdev: platform device pointer
 164 * Description: this function calls the main to free the net resources
 165 * and calls the platforms hook and release the resources (e.g. mem).
 166 */
 167static int stmmac_pltfr_remove(struct platform_device *pdev)
 168{
 169        struct net_device *ndev = platform_get_drvdata(pdev);
 170        struct stmmac_priv *priv = netdev_priv(ndev);
 171        int ret = stmmac_dvr_remove(ndev);
 172
 173        if (priv->plat->exit)
 174                priv->plat->exit(pdev);
 175
 176        platform_set_drvdata(pdev, NULL);
 177
 178        return ret;
 179}
 180
 181#ifdef CONFIG_PM
 182static int stmmac_pltfr_suspend(struct device *dev)
 183{
 184        struct net_device *ndev = dev_get_drvdata(dev);
 185
 186        return stmmac_suspend(ndev);
 187}
 188
 189static int stmmac_pltfr_resume(struct device *dev)
 190{
 191        struct net_device *ndev = dev_get_drvdata(dev);
 192
 193        return stmmac_resume(ndev);
 194}
 195
 196int stmmac_pltfr_freeze(struct device *dev)
 197{
 198        int ret;
 199        struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev);
 200        struct net_device *ndev = dev_get_drvdata(dev);
 201        struct platform_device *pdev = to_platform_device(dev);
 202
 203        ret = stmmac_freeze(ndev);
 204        if (plat_dat->exit)
 205                plat_dat->exit(pdev);
 206
 207        return ret;
 208}
 209
 210int stmmac_pltfr_restore(struct device *dev)
 211{
 212        struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev);
 213        struct net_device *ndev = dev_get_drvdata(dev);
 214        struct platform_device *pdev = to_platform_device(dev);
 215
 216        if (plat_dat->init)
 217                plat_dat->init(pdev);
 218
 219        return stmmac_restore(ndev);
 220}
 221
 222static const struct dev_pm_ops stmmac_pltfr_pm_ops = {
 223        .suspend = stmmac_pltfr_suspend,
 224        .resume = stmmac_pltfr_resume,
 225        .freeze = stmmac_pltfr_freeze,
 226        .thaw = stmmac_pltfr_restore,
 227        .restore = stmmac_pltfr_restore,
 228};
 229#else
 230static const struct dev_pm_ops stmmac_pltfr_pm_ops;
 231#endif /* CONFIG_PM */
 232
 233static const struct of_device_id stmmac_dt_ids[] = {
 234        { .compatible = "st,spear600-gmac"},
 235        { .compatible = "snps,dwmac-3.70a"},
 236        { .compatible = "snps,dwmac"},
 237        { /* sentinel */ }
 238};
 239MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
 240
 241struct platform_driver stmmac_pltfr_driver = {
 242        .probe = stmmac_pltfr_probe,
 243        .remove = stmmac_pltfr_remove,
 244        .driver = {
 245                   .name = STMMAC_RESOURCE_NAME,
 246                   .owner = THIS_MODULE,
 247                   .pm = &stmmac_pltfr_pm_ops,
 248                   .of_match_table = of_match_ptr(stmmac_dt_ids),
 249                   },
 250};
 251
 252MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver");
 253MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
 254MODULE_LICENSE("GPL");
 255