• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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