]> Pileus Git - ~andy/linux/blob - drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
net: stmmac: Use driver data and callbacks tied with compatible strings
[~andy/linux] / drivers / net / ethernet / stmicro / stmmac / stmmac_platform.c
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 <linux/of_device.h>
30 #include "stmmac.h"
31
32 static const struct of_device_id stmmac_dt_ids[] = {
33         /* SoC specific glue layers should come before generic bindings */
34         { .compatible = "st,spear600-gmac"},
35         { .compatible = "snps,dwmac-3.610"},
36         { .compatible = "snps,dwmac-3.70a"},
37         { .compatible = "snps,dwmac-3.710"},
38         { .compatible = "snps,dwmac"},
39         { /* sentinel */ }
40 };
41 MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
42
43 #ifdef CONFIG_OF
44 static int stmmac_probe_config_dt(struct platform_device *pdev,
45                                   struct plat_stmmacenet_data *plat,
46                                   const char **mac)
47 {
48         struct device_node *np = pdev->dev.of_node;
49         struct stmmac_dma_cfg *dma_cfg;
50         const struct of_device_id *device;
51
52         if (!np)
53                 return -ENODEV;
54
55         device = of_match_device(stmmac_dt_ids, &pdev->dev);
56         if (!device)
57                 return -ENODEV;
58
59         if (device->data) {
60                 const struct stmmac_of_data *data = device->data;
61                 plat->has_gmac = data->has_gmac;
62                 plat->enh_desc = data->enh_desc;
63                 plat->tx_coe = data->tx_coe;
64                 plat->rx_coe = data->rx_coe;
65                 plat->bugged_jumbo = data->bugged_jumbo;
66                 plat->pmt = data->pmt;
67                 plat->riwt_off = data->riwt_off;
68                 plat->fix_mac_speed = data->fix_mac_speed;
69                 plat->bus_setup = data->bus_setup;
70                 plat->setup = data->setup;
71                 plat->free = data->free;
72                 plat->init = data->init;
73                 plat->exit = data->exit;
74         }
75
76         *mac = of_get_mac_address(np);
77         plat->interface = of_get_phy_mode(np);
78
79         /* Get max speed of operation from device tree */
80         if (of_property_read_u32(np, "max-speed", &plat->max_speed))
81                 plat->max_speed = -1;
82
83         plat->bus_id = of_alias_get_id(np, "ethernet");
84         if (plat->bus_id < 0)
85                 plat->bus_id = 0;
86
87         /* Default to phy auto-detection */
88         plat->phy_addr = -1;
89
90         /* "snps,phy-addr" is not a standard property. Mark it as deprecated
91          * and warn of its use. Remove this when phy node support is added.
92          */
93         if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
94                 dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
95
96         plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
97                                            sizeof(struct stmmac_mdio_bus_data),
98                                            GFP_KERNEL);
99
100         plat->force_sf_dma_mode = of_property_read_bool(np, "snps,force_sf_dma_mode");
101
102         /*
103          * Currently only the properties needed on SPEAr600
104          * are provided. All other properties should be added
105          * once needed on other platforms.
106          */
107         if (of_device_is_compatible(np, "st,spear600-gmac") ||
108                 of_device_is_compatible(np, "snps,dwmac-3.70a") ||
109                 of_device_is_compatible(np, "snps,dwmac")) {
110                 plat->has_gmac = 1;
111                 plat->pmt = 1;
112         }
113
114         if (of_device_is_compatible(np, "snps,dwmac-3.610") ||
115                 of_device_is_compatible(np, "snps,dwmac-3.710")) {
116                 plat->enh_desc = 1;
117                 plat->bugged_jumbo = 1;
118                 plat->force_sf_dma_mode = 1;
119         }
120
121         if (of_find_property(np, "snps,pbl", NULL)) {
122                 dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
123                                        GFP_KERNEL);
124                 if (!dma_cfg)
125                         return -ENOMEM;
126                 plat->dma_cfg = dma_cfg;
127                 of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
128                 dma_cfg->fixed_burst =
129                         of_property_read_bool(np, "snps,fixed-burst");
130                 dma_cfg->mixed_burst =
131                         of_property_read_bool(np, "snps,mixed-burst");
132         }
133         plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode");
134         if (plat->force_thresh_dma_mode) {
135                 plat->force_sf_dma_mode = 0;
136                 pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set.");
137         }
138
139         return 0;
140 }
141 #else
142 static int stmmac_probe_config_dt(struct platform_device *pdev,
143                                   struct plat_stmmacenet_data *plat,
144                                   const char **mac)
145 {
146         return -ENOSYS;
147 }
148 #endif /* CONFIG_OF */
149
150 /**
151  * stmmac_pltfr_probe
152  * @pdev: platform device pointer
153  * Description: platform_device probe function. It allocates
154  * the necessary resources and invokes the main to init
155  * the net device, register the mdio bus etc.
156  */
157 static int stmmac_pltfr_probe(struct platform_device *pdev)
158 {
159         int ret = 0;
160         struct resource *res;
161         struct device *dev = &pdev->dev;
162         void __iomem *addr = NULL;
163         struct stmmac_priv *priv = NULL;
164         struct plat_stmmacenet_data *plat_dat = NULL;
165         const char *mac = NULL;
166
167         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
168         addr = devm_ioremap_resource(dev, res);
169         if (IS_ERR(addr))
170                 return PTR_ERR(addr);
171
172         plat_dat = dev_get_platdata(&pdev->dev);
173         if (pdev->dev.of_node) {
174                 if (!plat_dat)
175                         plat_dat = devm_kzalloc(&pdev->dev,
176                                         sizeof(struct plat_stmmacenet_data),
177                                         GFP_KERNEL);
178                 if (!plat_dat) {
179                         pr_err("%s: ERROR: no memory", __func__);
180                         return  -ENOMEM;
181                 }
182
183                 ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
184                 if (ret) {
185                         pr_err("%s: main dt probe failed", __func__);
186                         return ret;
187                 }
188         }
189
190         /* Custom setup (if needed) */
191         if (plat_dat->setup) {
192                 plat_dat->bsp_priv = plat_dat->setup(pdev);
193                 if (IS_ERR(plat_dat->bsp_priv))
194                         return PTR_ERR(plat_dat->bsp_priv);
195         }
196
197         /* Custom initialisation (if needed)*/
198         if (plat_dat->init) {
199                 ret = plat_dat->init(pdev, plat_dat->bsp_priv);
200                 if (unlikely(ret))
201                         return ret;
202         }
203
204         priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
205         if (IS_ERR(priv)) {
206                 pr_err("%s: main driver probe failed", __func__);
207                 return PTR_ERR(priv);
208         }
209
210         /* Get MAC address if available (DT) */
211         if (mac)
212                 memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
213
214         /* Get the MAC information */
215         priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
216         if (priv->dev->irq == -ENXIO) {
217                 pr_err("%s: ERROR: MAC IRQ configuration "
218                        "information not found\n", __func__);
219                 return -ENXIO;
220         }
221
222         /*
223          * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
224          * The external wake up irq can be passed through the platform code
225          * named as "eth_wake_irq"
226          *
227          * In case the wake up interrupt is not passed from the platform
228          * so the driver will continue to use the mac irq (ndev->irq)
229          */
230         priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
231         if (priv->wol_irq == -ENXIO)
232                 priv->wol_irq = priv->dev->irq;
233
234         priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
235
236         platform_set_drvdata(pdev, priv->dev);
237
238         pr_debug("STMMAC platform driver registration completed");
239
240         return 0;
241 }
242
243 /**
244  * stmmac_pltfr_remove
245  * @pdev: platform device pointer
246  * Description: this function calls the main to free the net resources
247  * and calls the platforms hook and release the resources (e.g. mem).
248  */
249 static int stmmac_pltfr_remove(struct platform_device *pdev)
250 {
251         struct net_device *ndev = platform_get_drvdata(pdev);
252         struct stmmac_priv *priv = netdev_priv(ndev);
253         int ret = stmmac_dvr_remove(ndev);
254
255         if (priv->plat->exit)
256                 priv->plat->exit(pdev, priv->plat->bsp_priv);
257
258         if (priv->plat->free)
259                 priv->plat->free(pdev, priv->plat->bsp_priv);
260
261         return ret;
262 }
263
264 #ifdef CONFIG_PM
265 static int stmmac_pltfr_suspend(struct device *dev)
266 {
267         int ret;
268         struct net_device *ndev = dev_get_drvdata(dev);
269         struct stmmac_priv *priv = netdev_priv(ndev);
270         struct platform_device *pdev = to_platform_device(dev);
271
272         ret = stmmac_suspend(ndev);
273         if (priv->plat->exit)
274                 priv->plat->exit(pdev, priv->plat->bsp_priv);
275
276         return ret;
277 }
278
279 static int stmmac_pltfr_resume(struct device *dev)
280 {
281         struct net_device *ndev = dev_get_drvdata(dev);
282         struct stmmac_priv *priv = netdev_priv(ndev);
283         struct platform_device *pdev = to_platform_device(dev);
284
285         if (priv->plat->init)
286                 priv->plat->init(pdev, priv->plat->bsp_priv);
287
288         return stmmac_resume(ndev);
289 }
290
291 #endif /* CONFIG_PM */
292
293 static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops,
294                         stmmac_pltfr_suspend, stmmac_pltfr_resume);
295
296 struct platform_driver stmmac_pltfr_driver = {
297         .probe = stmmac_pltfr_probe,
298         .remove = stmmac_pltfr_remove,
299         .driver = {
300                    .name = STMMAC_RESOURCE_NAME,
301                    .owner = THIS_MODULE,
302                    .pm = &stmmac_pltfr_pm_ops,
303                    .of_match_table = of_match_ptr(stmmac_dt_ids),
304                    },
305 };
306
307 MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver");
308 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
309 MODULE_LICENSE("GPL");