1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* 10G controller driver for Samsung SoCs |
3 | * |
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. |
5 | * http://www.samsung.com |
6 | * |
7 | * Author: Siva Reddy Kallam <siva.kallam@samsung.com> |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/etherdevice.h> |
13 | #include <linux/io.h> |
14 | #include <linux/module.h> |
15 | #include <linux/netdevice.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_irq.h> |
18 | #include <linux/of_net.h> |
19 | #include <linux/phy.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/sxgbe_platform.h> |
22 | |
23 | #include "sxgbe_common.h" |
24 | #include "sxgbe_reg.h" |
25 | |
26 | #ifdef CONFIG_OF |
27 | static int sxgbe_probe_config_dt(struct platform_device *pdev, |
28 | struct sxgbe_plat_data *plat) |
29 | { |
30 | struct device_node *np = pdev->dev.of_node; |
31 | struct sxgbe_dma_cfg *dma_cfg; |
32 | int err; |
33 | |
34 | if (!np) |
35 | return -ENODEV; |
36 | |
37 | err = of_get_phy_mode(np, interface: &plat->interface); |
38 | if (err && err != -ENODEV) |
39 | return err; |
40 | |
41 | plat->bus_id = of_alias_get_id(np, stem: "ethernet" ); |
42 | if (plat->bus_id < 0) |
43 | plat->bus_id = 0; |
44 | |
45 | plat->mdio_bus_data = devm_kzalloc(dev: &pdev->dev, |
46 | size: sizeof(*plat->mdio_bus_data), |
47 | GFP_KERNEL); |
48 | if (!plat->mdio_bus_data) |
49 | return -ENOMEM; |
50 | |
51 | dma_cfg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dma_cfg), GFP_KERNEL); |
52 | if (!dma_cfg) |
53 | return -ENOMEM; |
54 | |
55 | plat->dma_cfg = dma_cfg; |
56 | of_property_read_u32(np, propname: "samsung,pbl" , out_value: &dma_cfg->pbl); |
57 | if (of_property_read_u32(np, propname: "samsung,burst-map" , out_value: &dma_cfg->burst_map) == 0) |
58 | dma_cfg->fixed_burst = true; |
59 | |
60 | return 0; |
61 | } |
62 | #else |
63 | static int sxgbe_probe_config_dt(struct platform_device *pdev, |
64 | struct sxgbe_plat_data *plat) |
65 | { |
66 | return -ENOSYS; |
67 | } |
68 | #endif /* CONFIG_OF */ |
69 | |
70 | /** |
71 | * sxgbe_platform_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 | */ |
77 | static int sxgbe_platform_probe(struct platform_device *pdev) |
78 | { |
79 | int ret; |
80 | int i, chan; |
81 | struct device *dev = &pdev->dev; |
82 | void __iomem *addr; |
83 | struct sxgbe_priv_data *priv = NULL; |
84 | struct sxgbe_plat_data *plat_dat = NULL; |
85 | struct net_device *ndev = platform_get_drvdata(pdev); |
86 | struct device_node *node = dev->of_node; |
87 | |
88 | /* Get memory resource */ |
89 | addr = devm_platform_ioremap_resource(pdev, index: 0); |
90 | if (IS_ERR(ptr: addr)) |
91 | return PTR_ERR(ptr: addr); |
92 | |
93 | if (pdev->dev.of_node) { |
94 | plat_dat = devm_kzalloc(dev: &pdev->dev, |
95 | size: sizeof(struct sxgbe_plat_data), |
96 | GFP_KERNEL); |
97 | if (!plat_dat) |
98 | return -ENOMEM; |
99 | |
100 | ret = sxgbe_probe_config_dt(pdev, plat: plat_dat); |
101 | if (ret) { |
102 | pr_err("%s: main dt probe failed\n" , __func__); |
103 | return ret; |
104 | } |
105 | } |
106 | |
107 | priv = sxgbe_drv_probe(device: &(pdev->dev), plat_dat, addr); |
108 | if (!priv) { |
109 | pr_err("%s: main driver probe failed\n" , __func__); |
110 | goto err_out; |
111 | } |
112 | |
113 | /* Get the SXGBE common INT information */ |
114 | priv->irq = irq_of_parse_and_map(node, index: 0); |
115 | if (priv->irq <= 0) { |
116 | dev_err(dev, "sxgbe common irq parsing failed\n" ); |
117 | goto err_drv_remove; |
118 | } |
119 | |
120 | /* Get MAC address if available (DT) */ |
121 | of_get_ethdev_address(np: node, dev: priv->dev); |
122 | |
123 | /* Get the TX/RX IRQ numbers */ |
124 | for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) { |
125 | priv->txq[i]->irq_no = irq_of_parse_and_map(node, index: chan++); |
126 | if (priv->txq[i]->irq_no <= 0) { |
127 | dev_err(dev, "sxgbe tx irq parsing failed\n" ); |
128 | goto err_tx_irq_unmap; |
129 | } |
130 | } |
131 | |
132 | for (i = 0; i < SXGBE_RX_QUEUES; i++) { |
133 | priv->rxq[i]->irq_no = irq_of_parse_and_map(node, index: chan++); |
134 | if (priv->rxq[i]->irq_no <= 0) { |
135 | dev_err(dev, "sxgbe rx irq parsing failed\n" ); |
136 | goto err_rx_irq_unmap; |
137 | } |
138 | } |
139 | |
140 | priv->lpi_irq = irq_of_parse_and_map(node, index: chan); |
141 | if (priv->lpi_irq <= 0) { |
142 | dev_err(dev, "sxgbe lpi irq parsing failed\n" ); |
143 | goto err_rx_irq_unmap; |
144 | } |
145 | |
146 | platform_set_drvdata(pdev, data: priv->dev); |
147 | |
148 | pr_debug("platform driver registration completed\n" ); |
149 | |
150 | return 0; |
151 | |
152 | err_rx_irq_unmap: |
153 | while (i--) |
154 | irq_dispose_mapping(virq: priv->rxq[i]->irq_no); |
155 | i = SXGBE_TX_QUEUES; |
156 | err_tx_irq_unmap: |
157 | while (i--) |
158 | irq_dispose_mapping(virq: priv->txq[i]->irq_no); |
159 | irq_dispose_mapping(virq: priv->irq); |
160 | err_drv_remove: |
161 | sxgbe_drv_remove(ndev); |
162 | err_out: |
163 | return -ENODEV; |
164 | } |
165 | |
166 | /** |
167 | * sxgbe_platform_remove |
168 | * @pdev: platform device pointer |
169 | * Description: this function calls the main to free the net resources |
170 | * and calls the platforms hook and release the resources (e.g. mem). |
171 | */ |
172 | static void sxgbe_platform_remove(struct platform_device *pdev) |
173 | { |
174 | struct net_device *ndev = platform_get_drvdata(pdev); |
175 | |
176 | sxgbe_drv_remove(ndev); |
177 | } |
178 | |
179 | #ifdef CONFIG_PM |
180 | static int sxgbe_platform_suspend(struct device *dev) |
181 | { |
182 | struct net_device *ndev = dev_get_drvdata(dev); |
183 | |
184 | return sxgbe_suspend(ndev); |
185 | } |
186 | |
187 | static int sxgbe_platform_resume(struct device *dev) |
188 | { |
189 | struct net_device *ndev = dev_get_drvdata(dev); |
190 | |
191 | return sxgbe_resume(ndev); |
192 | } |
193 | |
194 | static int sxgbe_platform_freeze(struct device *dev) |
195 | { |
196 | struct net_device *ndev = dev_get_drvdata(dev); |
197 | |
198 | return sxgbe_freeze(ndev); |
199 | } |
200 | |
201 | static int sxgbe_platform_restore(struct device *dev) |
202 | { |
203 | struct net_device *ndev = dev_get_drvdata(dev); |
204 | |
205 | return sxgbe_restore(ndev); |
206 | } |
207 | |
208 | static const struct dev_pm_ops sxgbe_platform_pm_ops = { |
209 | .suspend = sxgbe_platform_suspend, |
210 | .resume = sxgbe_platform_resume, |
211 | .freeze = sxgbe_platform_freeze, |
212 | .thaw = sxgbe_platform_restore, |
213 | .restore = sxgbe_platform_restore, |
214 | }; |
215 | #else |
216 | static const struct dev_pm_ops sxgbe_platform_pm_ops; |
217 | #endif /* CONFIG_PM */ |
218 | |
219 | static const struct of_device_id sxgbe_dt_ids[] = { |
220 | { .compatible = "samsung,sxgbe-v2.0a" }, |
221 | { /* sentinel */ } |
222 | }; |
223 | MODULE_DEVICE_TABLE(of, sxgbe_dt_ids); |
224 | |
225 | static struct platform_driver sxgbe_platform_driver = { |
226 | .probe = sxgbe_platform_probe, |
227 | .remove_new = sxgbe_platform_remove, |
228 | .driver = { |
229 | .name = SXGBE_RESOURCE_NAME, |
230 | .pm = &sxgbe_platform_pm_ops, |
231 | .of_match_table = sxgbe_dt_ids, |
232 | }, |
233 | }; |
234 | |
235 | int sxgbe_register_platform(void) |
236 | { |
237 | int err; |
238 | |
239 | err = platform_driver_register(&sxgbe_platform_driver); |
240 | if (err) |
241 | pr_err("failed to register the platform driver\n" ); |
242 | |
243 | return err; |
244 | } |
245 | |
246 | void sxgbe_unregister_platform(void) |
247 | { |
248 | platform_driver_unregister(&sxgbe_platform_driver); |
249 | } |
250 | |