1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020, Loongson Corporation
3 */
4
5 #include <linux/clk-provider.h>
6 #include <linux/pci.h>
7 #include <linux/dmi.h>
8 #include <linux/device.h>
9 #include <linux/of_irq.h>
10 #include "stmmac.h"
11
12 struct stmmac_pci_info {
13 int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
14 };
15
common_default_data(struct pci_dev * pdev,struct plat_stmmacenet_data * plat)16 static void common_default_data(struct pci_dev *pdev,
17 struct plat_stmmacenet_data *plat)
18 {
19 plat->bus_id = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
20 plat->interface = PHY_INTERFACE_MODE_GMII;
21
22 plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
23 plat->has_gmac = 1;
24 plat->force_sf_dma_mode = 1;
25
26 /* Set default value for multicast hash bins */
27 plat->multicast_filter_bins = 256;
28
29 /* Set default value for unicast filter entries */
30 plat->unicast_filter_entries = 1;
31
32 /* Set the maxmtu to a default of JUMBO_LEN */
33 plat->maxmtu = JUMBO_LEN;
34
35 /* Set default number of RX and TX queues to use */
36 plat->tx_queues_to_use = 1;
37 plat->rx_queues_to_use = 1;
38
39 /* Disable Priority config by default */
40 plat->tx_queues_cfg[0].use_prio = false;
41 plat->rx_queues_cfg[0].use_prio = false;
42
43 /* Disable RX queues routing by default */
44 plat->rx_queues_cfg[0].pkt_route = 0x0;
45
46 plat->dma_cfg->pbl = 32;
47 plat->dma_cfg->pblx8 = true;
48
49 plat->clk_ref_rate = 125000000;
50 plat->clk_ptp_rate = 125000000;
51 }
52
loongson_gmac_data(struct pci_dev * pdev,struct plat_stmmacenet_data * plat)53 static int loongson_gmac_data(struct pci_dev *pdev,
54 struct plat_stmmacenet_data *plat)
55 {
56 common_default_data(pdev, plat);
57
58 plat->mdio_bus_data->phy_mask = 0;
59
60 plat->phy_addr = -1;
61 plat->phy_interface = PHY_INTERFACE_MODE_RGMII_ID;
62
63 return 0;
64 }
65
66 static struct stmmac_pci_info loongson_gmac_pci_info = {
67 .setup = loongson_gmac_data,
68 };
69
loongson_gnet_fix_speed(void * priv,unsigned int speed)70 static void loongson_gnet_fix_speed(void *priv, unsigned int speed)
71 {
72 struct net_device *ndev = (struct net_device *)(*(unsigned long *)priv);
73 struct stmmac_priv *ptr = netdev_priv(ndev);
74
75 if (speed == SPEED_1000) {
76 if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15) /* PS */) {
77 /* reset phy */
78 phy_set_bits(ndev->phydev, 0 /*MII_BMCR*/,
79 0x200 /*BMCR_ANRESTART*/);
80 }
81 }
82 }
83
loongson_gnet_data(struct pci_dev * pdev,struct plat_stmmacenet_data * plat)84 static int loongson_gnet_data(struct pci_dev *pdev,
85 struct plat_stmmacenet_data *plat)
86 {
87 common_default_data(pdev, plat);
88
89 plat->mdio_bus_data->phy_mask = 0xfffffffb;
90
91 plat->phy_addr = 2;
92 plat->phy_interface = PHY_INTERFACE_MODE_GMII;
93
94 /* GNET 1000M speed need workaround */
95 plat->fix_mac_speed = loongson_gnet_fix_speed;
96
97 /* Get netdev pointer address */
98 plat->bsp_priv = &(pdev->dev.driver_data);
99
100 return 0;
101 }
102
103 static struct stmmac_pci_info loongson_gnet_pci_info = {
104 .setup = loongson_gnet_data,
105 };
106
loongson_dwmac_probe(struct pci_dev * pdev,const struct pci_device_id * id)107 static int loongson_dwmac_probe(struct pci_dev *pdev,
108 const struct pci_device_id *id)
109 {
110 struct plat_stmmacenet_data *plat;
111 struct stmmac_pci_info *info;
112 struct stmmac_resources res;
113 struct device_node *np;
114 int ret, i, bus_id, phy_mode;
115 bool mdio = false;
116
117 np = dev_of_node(&pdev->dev);
118 if (np && !of_device_is_compatible(np, "loongson, pci-gmac")) {
119 pr_info("dwmac_loongson_pci: Incompatible OF node\n");
120 return -ENODEV;
121 }
122
123 plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
124 if (!plat)
125 return -ENOMEM;
126
127 if (plat->mdio_node) {
128 dev_err(&pdev->dev, "Found MDIO subnode\n");
129 mdio = true;
130 }
131
132 plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
133 sizeof(*plat->mdio_bus_data),
134 GFP_KERNEL);
135 if (!plat->mdio_bus_data)
136 return -ENOMEM;
137
138 if (mdio)
139 plat->mdio_bus_data->needs_reset = true;
140
141 plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL);
142 if (!plat->dma_cfg)
143 return -ENOMEM;
144
145 /* Enable pci device */
146 ret = pci_enable_device(pdev);
147 if (ret) {
148 dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__);
149 return ret;
150 }
151
152 /* Get the base address of device */
153 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
154 if (pci_resource_len(pdev, i) == 0)
155 continue;
156 ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
157 if (ret)
158 return ret;
159 break;
160 }
161
162 pci_set_master(pdev);
163
164 info = (struct stmmac_pci_info *)id->driver_data;
165 ret = info->setup(pdev, plat);
166 if (ret)
167 return ret;
168
169 if (np) {
170 bus_id = of_alias_get_id(np, "ethernet");
171 if (bus_id >= 0)
172 plat->bus_id = bus_id;
173
174 phy_mode = device_get_phy_mode(&pdev->dev);
175 if (phy_mode < 0) {
176 dev_err(&pdev->dev, "phy_mode not found\n");
177 return phy_mode;
178 }
179 plat->phy_interface = phy_mode;
180 }
181
182 pci_enable_msi(pdev);
183
184 memset(&res, 0, sizeof(res));
185 res.addr = pcim_iomap_table(pdev)[0];
186 if (np) {
187 res.irq = of_irq_get_byname(np, "macirq");
188 if (res.irq < 0) {
189 dev_err(&pdev->dev, "IRQ macirq not found\n");
190 ret = -ENODEV;
191 }
192
193 res.wol_irq = of_irq_get_byname(np, "eth_wake_irq");
194 if (res.wol_irq < 0) {
195 dev_info(&pdev->dev,
196 "IRQ eth_wake_irq not found, using macirq\n");
197 res.wol_irq = res.irq;
198 }
199
200 res.lpi_irq = of_irq_get_byname(np, "eth_lpi");
201 if (res.lpi_irq < 0) {
202 dev_err(&pdev->dev, "IRQ eth_lpi not found\n");
203 ret = -ENODEV;
204 }
205 } else {
206 res.irq = pdev->irq;
207 res.wol_irq = pdev->irq;
208 }
209
210 return stmmac_dvr_probe(&pdev->dev, plat, &res);
211 }
212
loongson_dwmac_remove(struct pci_dev * pdev)213 static void loongson_dwmac_remove(struct pci_dev *pdev)
214 {
215 int i;
216
217 stmmac_dvr_remove(&pdev->dev);
218
219 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
220 if (pci_resource_len(pdev, i) == 0)
221 continue;
222 pcim_iounmap_regions(pdev, BIT(i));
223 break;
224 }
225
226 pci_disable_device(pdev);
227 }
228
loongson_dwmac_suspend(struct device * dev)229 static int __maybe_unused loongson_dwmac_suspend(struct device *dev)
230 {
231 struct pci_dev *pdev = to_pci_dev(dev);
232 int ret;
233
234 ret = stmmac_suspend(dev);
235 if (ret)
236 return ret;
237
238 ret = pci_save_state(pdev);
239 if (ret)
240 return ret;
241
242 pci_disable_device(pdev);
243 pci_wake_from_d3(pdev, true);
244 return 0;
245 }
246
loongson_dwmac_resume(struct device * dev)247 static int __maybe_unused loongson_dwmac_resume(struct device *dev)
248 {
249 struct pci_dev *pdev = to_pci_dev(dev);
250 int ret;
251
252 pci_restore_state(pdev);
253 pci_set_power_state(pdev, PCI_D0);
254
255 ret = pci_enable_device(pdev);
256 if (ret)
257 return ret;
258
259 pci_set_master(pdev);
260
261 return stmmac_resume(dev);
262 }
263
264 static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend,
265 loongson_dwmac_resume);
266
267 #define PCI_DEVICE_ID_LOONGSON_GMAC 0x7a03
268 #define PCI_DEVICE_ID_LOONGSON_GNET 0x7a13
269
270 static const struct pci_device_id loongson_dwmac_id_table[] = {
271 { PCI_DEVICE_DATA(LOONGSON, GMAC, &loongson_gmac_pci_info) },
272 { PCI_DEVICE_DATA(LOONGSON, GNET, &loongson_gnet_pci_info) },
273 {}
274 };
275 MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table);
276
277 struct pci_driver loongson_dwmac_driver = {
278 .name = "dwmac-loongson-pci",
279 .id_table = loongson_dwmac_id_table,
280 .probe = loongson_dwmac_probe,
281 .remove = loongson_dwmac_remove,
282 .driver = {
283 .pm = &loongson_dwmac_pm_ops,
284 },
285 };
286
287 module_pci_driver(loongson_dwmac_driver);
288
289 MODULE_DESCRIPTION("Loongson DWMAC PCI driver");
290 MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>");
291 MODULE_LICENSE("GPL v2");
292