Commit | Line | Data |
---|---|---|
30bba69d QZ |
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 | static int loongson_default_data(struct plat_stmmacenet_data *plat) | |
13 | { | |
14 | plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ | |
15 | plat->has_gmac = 1; | |
16 | plat->force_sf_dma_mode = 1; | |
17 | ||
18 | /* Set default value for multicast hash bins */ | |
19 | plat->multicast_filter_bins = HASH_TABLE_SIZE; | |
20 | ||
21 | /* Set default value for unicast filter entries */ | |
22 | plat->unicast_filter_entries = 1; | |
23 | ||
24 | /* Set the maxmtu to a default of JUMBO_LEN */ | |
25 | plat->maxmtu = JUMBO_LEN; | |
26 | ||
27 | /* Set default number of RX and TX queues to use */ | |
28 | plat->tx_queues_to_use = 1; | |
29 | plat->rx_queues_to_use = 1; | |
30 | ||
31 | /* Disable Priority config by default */ | |
32 | plat->tx_queues_cfg[0].use_prio = false; | |
33 | plat->rx_queues_cfg[0].use_prio = false; | |
34 | ||
35 | /* Disable RX queues routing by default */ | |
36 | plat->rx_queues_cfg[0].pkt_route = 0x0; | |
37 | ||
38 | /* Default to phy auto-detection */ | |
39 | plat->phy_addr = -1; | |
40 | ||
41 | plat->dma_cfg->pbl = 32; | |
42 | plat->dma_cfg->pblx8 = true; | |
43 | ||
44 | plat->multicast_filter_bins = 256; | |
45 | return 0; | |
46 | } | |
47 | ||
48 | static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |
49 | { | |
50 | struct plat_stmmacenet_data *plat; | |
51 | struct stmmac_resources res; | |
30bba69d | 52 | struct device_node *np; |
0d472c69 Y |
53 | int ret, i, phy_mode; |
54 | bool mdio = false; | |
30bba69d QZ |
55 | |
56 | np = dev_of_node(&pdev->dev); | |
57 | ||
58 | if (!np) { | |
59 | pr_info("dwmac_loongson_pci: No OF node\n"); | |
60 | return -ENODEV; | |
61 | } | |
62 | ||
63 | if (!of_device_is_compatible(np, "loongson, pci-gmac")) { | |
64 | pr_info("dwmac_loongson_pci: Incompatible OF node\n"); | |
65 | return -ENODEV; | |
66 | } | |
67 | ||
68 | plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); | |
69 | if (!plat) | |
70 | return -ENOMEM; | |
71 | ||
72 | if (plat->mdio_node) { | |
73 | dev_err(&pdev->dev, "Found MDIO subnode\n"); | |
74 | mdio = true; | |
75 | } | |
76 | ||
77 | if (mdio) { | |
78 | plat->mdio_bus_data = devm_kzalloc(&pdev->dev, | |
79 | sizeof(*plat->mdio_bus_data), | |
80 | GFP_KERNEL); | |
81 | if (!plat->mdio_bus_data) | |
82 | return -ENOMEM; | |
83 | plat->mdio_bus_data->needs_reset = true; | |
84 | } | |
85 | ||
86 | plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); | |
87 | if (!plat->dma_cfg) | |
88 | return -ENOMEM; | |
89 | ||
90 | /* Enable pci device */ | |
91 | ret = pci_enable_device(pdev); | |
92 | if (ret) { | |
93 | dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__); | |
94 | return ret; | |
95 | } | |
96 | ||
97 | /* Get the base address of device */ | |
98 | for (i = 0; i < PCI_STD_NUM_BARS; i++) { | |
99 | if (pci_resource_len(pdev, i) == 0) | |
100 | continue; | |
101 | ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); | |
102 | if (ret) | |
103 | return ret; | |
104 | break; | |
105 | } | |
106 | ||
107 | plat->bus_id = of_alias_get_id(np, "ethernet"); | |
108 | if (plat->bus_id < 0) | |
109 | plat->bus_id = pci_dev_id(pdev); | |
110 | ||
0d472c69 | 111 | phy_mode = device_get_phy_mode(&pdev->dev); |
5289de59 | 112 | if (phy_mode < 0) { |
30bba69d | 113 | dev_err(&pdev->dev, "phy_mode not found\n"); |
5289de59 | 114 | return phy_mode; |
115 | } | |
30bba69d | 116 | |
0d472c69 | 117 | plat->phy_interface = phy_mode; |
30bba69d QZ |
118 | plat->interface = PHY_INTERFACE_MODE_GMII; |
119 | ||
120 | pci_set_master(pdev); | |
121 | ||
122 | loongson_default_data(plat); | |
123 | pci_enable_msi(pdev); | |
124 | memset(&res, 0, sizeof(res)); | |
125 | res.addr = pcim_iomap_table(pdev)[0]; | |
126 | ||
127 | res.irq = of_irq_get_byname(np, "macirq"); | |
128 | if (res.irq < 0) { | |
129 | dev_err(&pdev->dev, "IRQ macirq not found\n"); | |
130 | ret = -ENODEV; | |
131 | } | |
132 | ||
133 | res.wol_irq = of_irq_get_byname(np, "eth_wake_irq"); | |
134 | if (res.wol_irq < 0) { | |
135 | dev_info(&pdev->dev, "IRQ eth_wake_irq not found, using macirq\n"); | |
136 | res.wol_irq = res.irq; | |
137 | } | |
138 | ||
139 | res.lpi_irq = of_irq_get_byname(np, "eth_lpi"); | |
140 | if (res.lpi_irq < 0) { | |
141 | dev_err(&pdev->dev, "IRQ eth_lpi not found\n"); | |
142 | ret = -ENODEV; | |
143 | } | |
144 | ||
145 | return stmmac_dvr_probe(&pdev->dev, plat, &res); | |
146 | } | |
147 | ||
148 | static void loongson_dwmac_remove(struct pci_dev *pdev) | |
149 | { | |
150 | int i; | |
151 | ||
152 | stmmac_dvr_remove(&pdev->dev); | |
153 | ||
154 | for (i = 0; i < PCI_STD_NUM_BARS; i++) { | |
155 | if (pci_resource_len(pdev, i) == 0) | |
156 | continue; | |
157 | pcim_iounmap_regions(pdev, BIT(i)); | |
158 | break; | |
159 | } | |
160 | ||
161 | pci_disable_device(pdev); | |
162 | } | |
163 | ||
164 | static int __maybe_unused loongson_dwmac_suspend(struct device *dev) | |
165 | { | |
166 | struct pci_dev *pdev = to_pci_dev(dev); | |
167 | int ret; | |
168 | ||
169 | ret = stmmac_suspend(dev); | |
170 | if (ret) | |
171 | return ret; | |
172 | ||
173 | ret = pci_save_state(pdev); | |
174 | if (ret) | |
175 | return ret; | |
176 | ||
177 | pci_disable_device(pdev); | |
178 | pci_wake_from_d3(pdev, true); | |
179 | return 0; | |
180 | } | |
181 | ||
182 | static int __maybe_unused loongson_dwmac_resume(struct device *dev) | |
183 | { | |
184 | struct pci_dev *pdev = to_pci_dev(dev); | |
185 | int ret; | |
186 | ||
187 | pci_restore_state(pdev); | |
188 | pci_set_power_state(pdev, PCI_D0); | |
189 | ||
190 | ret = pci_enable_device(pdev); | |
191 | if (ret) | |
192 | return ret; | |
193 | ||
194 | pci_set_master(pdev); | |
195 | ||
196 | return stmmac_resume(dev); | |
197 | } | |
198 | ||
199 | static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend, | |
200 | loongson_dwmac_resume); | |
201 | ||
202 | static const struct pci_device_id loongson_dwmac_id_table[] = { | |
203 | { PCI_VDEVICE(LOONGSON, 0x7a03) }, | |
204 | {} | |
205 | }; | |
206 | MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table); | |
207 | ||
2baed4f9 | 208 | static struct pci_driver loongson_dwmac_driver = { |
30bba69d QZ |
209 | .name = "dwmac-loongson-pci", |
210 | .id_table = loongson_dwmac_id_table, | |
211 | .probe = loongson_dwmac_probe, | |
212 | .remove = loongson_dwmac_remove, | |
213 | .driver = { | |
214 | .pm = &loongson_dwmac_pm_ops, | |
215 | }, | |
216 | }; | |
217 | ||
218 | module_pci_driver(loongson_dwmac_driver); | |
219 | ||
220 | MODULE_DESCRIPTION("Loongson DWMAC PCI driver"); | |
221 | MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>"); | |
222 | MODULE_LICENSE("GPL v2"); |