1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /******************************************************************************* |
3 | This contains the functions to handle the pci driver. |
4 | |
5 | Copyright (C) 2011-2012 Vayavya Labs Pvt Ltd |
6 | |
7 | |
8 | Author: Rayagond Kokatanur <rayagond@vayavyalabs.com> |
9 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
10 | *******************************************************************************/ |
11 | |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/pci.h> |
14 | #include <linux/dmi.h> |
15 | |
16 | #include "stmmac.h" |
17 | |
18 | struct stmmac_pci_info { |
19 | int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat); |
20 | }; |
21 | |
22 | static void common_default_data(struct plat_stmmacenet_data *plat) |
23 | { |
24 | plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ |
25 | plat->has_gmac = 1; |
26 | plat->force_sf_dma_mode = 1; |
27 | |
28 | plat->mdio_bus_data->needs_reset = true; |
29 | |
30 | /* Set default value for multicast hash bins */ |
31 | plat->multicast_filter_bins = HASH_TABLE_SIZE; |
32 | |
33 | /* Set default value for unicast filter entries */ |
34 | plat->unicast_filter_entries = 1; |
35 | |
36 | /* Set the maxmtu to a default of JUMBO_LEN */ |
37 | plat->maxmtu = JUMBO_LEN; |
38 | |
39 | /* Set default number of RX and TX queues to use */ |
40 | plat->tx_queues_to_use = 1; |
41 | plat->rx_queues_to_use = 1; |
42 | |
43 | /* Disable Priority config by default */ |
44 | plat->tx_queues_cfg[0].use_prio = false; |
45 | plat->rx_queues_cfg[0].use_prio = false; |
46 | |
47 | /* Disable RX queues routing by default */ |
48 | plat->rx_queues_cfg[0].pkt_route = 0x0; |
49 | } |
50 | |
51 | static int stmmac_default_data(struct pci_dev *pdev, |
52 | struct plat_stmmacenet_data *plat) |
53 | { |
54 | /* Set common default data first */ |
55 | common_default_data(plat); |
56 | |
57 | plat->bus_id = 1; |
58 | plat->phy_addr = 0; |
59 | plat->phy_interface = PHY_INTERFACE_MODE_GMII; |
60 | |
61 | plat->dma_cfg->pbl = 32; |
62 | plat->dma_cfg->pblx8 = true; |
63 | /* TODO: AXI */ |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static const struct stmmac_pci_info stmmac_pci_info = { |
69 | .setup = stmmac_default_data, |
70 | }; |
71 | |
72 | static int snps_gmac5_default_data(struct pci_dev *pdev, |
73 | struct plat_stmmacenet_data *plat) |
74 | { |
75 | int i; |
76 | |
77 | plat->clk_csr = 5; |
78 | plat->has_gmac4 = 1; |
79 | plat->force_sf_dma_mode = 1; |
80 | plat->flags |= STMMAC_FLAG_TSO_EN; |
81 | plat->pmt = 1; |
82 | |
83 | /* Set default value for multicast hash bins */ |
84 | plat->multicast_filter_bins = HASH_TABLE_SIZE; |
85 | |
86 | /* Set default value for unicast filter entries */ |
87 | plat->unicast_filter_entries = 1; |
88 | |
89 | /* Set the maxmtu to a default of JUMBO_LEN */ |
90 | plat->maxmtu = JUMBO_LEN; |
91 | |
92 | /* Set default number of RX and TX queues to use */ |
93 | plat->tx_queues_to_use = 4; |
94 | plat->rx_queues_to_use = 4; |
95 | |
96 | plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR; |
97 | for (i = 0; i < plat->tx_queues_to_use; i++) { |
98 | plat->tx_queues_cfg[i].use_prio = false; |
99 | plat->tx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB; |
100 | plat->tx_queues_cfg[i].weight = 25; |
101 | if (i > 0) |
102 | plat->tx_queues_cfg[i].tbs_en = 1; |
103 | } |
104 | |
105 | plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP; |
106 | for (i = 0; i < plat->rx_queues_to_use; i++) { |
107 | plat->rx_queues_cfg[i].use_prio = false; |
108 | plat->rx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB; |
109 | plat->rx_queues_cfg[i].pkt_route = 0x0; |
110 | plat->rx_queues_cfg[i].chan = i; |
111 | } |
112 | |
113 | plat->bus_id = 1; |
114 | plat->phy_addr = -1; |
115 | plat->phy_interface = PHY_INTERFACE_MODE_GMII; |
116 | |
117 | plat->dma_cfg->pbl = 32; |
118 | plat->dma_cfg->pblx8 = true; |
119 | |
120 | /* Axi Configuration */ |
121 | plat->axi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat->axi), GFP_KERNEL); |
122 | if (!plat->axi) |
123 | return -ENOMEM; |
124 | |
125 | plat->axi->axi_wr_osr_lmt = 31; |
126 | plat->axi->axi_rd_osr_lmt = 31; |
127 | |
128 | plat->axi->axi_fb = false; |
129 | plat->axi->axi_blen[0] = 4; |
130 | plat->axi->axi_blen[1] = 8; |
131 | plat->axi->axi_blen[2] = 16; |
132 | plat->axi->axi_blen[3] = 32; |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static const struct stmmac_pci_info snps_gmac5_pci_info = { |
138 | .setup = snps_gmac5_default_data, |
139 | }; |
140 | |
141 | /** |
142 | * stmmac_pci_probe |
143 | * |
144 | * @pdev: pci device pointer |
145 | * @id: pointer to table of device id/id's. |
146 | * |
147 | * Description: This probing function gets called for all PCI devices which |
148 | * match the ID table and are not "owned" by other driver yet. This function |
149 | * gets passed a "struct pci_dev *" for each device whose entry in the ID table |
150 | * matches the device. The probe functions returns zero when the driver choose |
151 | * to take "ownership" of the device or an error code(-ve no) otherwise. |
152 | */ |
153 | static int stmmac_pci_probe(struct pci_dev *pdev, |
154 | const struct pci_device_id *id) |
155 | { |
156 | struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; |
157 | struct plat_stmmacenet_data *plat; |
158 | struct stmmac_resources res; |
159 | int i; |
160 | int ret; |
161 | |
162 | plat = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat), GFP_KERNEL); |
163 | if (!plat) |
164 | return -ENOMEM; |
165 | |
166 | plat->mdio_bus_data = devm_kzalloc(dev: &pdev->dev, |
167 | size: sizeof(*plat->mdio_bus_data), |
168 | GFP_KERNEL); |
169 | if (!plat->mdio_bus_data) |
170 | return -ENOMEM; |
171 | |
172 | plat->dma_cfg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat->dma_cfg), |
173 | GFP_KERNEL); |
174 | if (!plat->dma_cfg) |
175 | return -ENOMEM; |
176 | |
177 | plat->safety_feat_cfg = devm_kzalloc(dev: &pdev->dev, |
178 | size: sizeof(*plat->safety_feat_cfg), |
179 | GFP_KERNEL); |
180 | if (!plat->safety_feat_cfg) |
181 | return -ENOMEM; |
182 | |
183 | /* Enable pci device */ |
184 | ret = pcim_enable_device(pdev); |
185 | if (ret) { |
186 | dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n" , |
187 | __func__); |
188 | return ret; |
189 | } |
190 | |
191 | /* Get the base address of device */ |
192 | for (i = 0; i < PCI_STD_NUM_BARS; i++) { |
193 | if (pci_resource_len(pdev, i) == 0) |
194 | continue; |
195 | ret = pcim_iomap_regions(pdev, BIT(i), name: pci_name(pdev)); |
196 | if (ret) |
197 | return ret; |
198 | break; |
199 | } |
200 | |
201 | pci_set_master(dev: pdev); |
202 | |
203 | ret = info->setup(pdev, plat); |
204 | if (ret) |
205 | return ret; |
206 | |
207 | memset(&res, 0, sizeof(res)); |
208 | res.addr = pcim_iomap_table(pdev)[i]; |
209 | res.wol_irq = pdev->irq; |
210 | res.irq = pdev->irq; |
211 | |
212 | plat->safety_feat_cfg->tsoee = 1; |
213 | plat->safety_feat_cfg->mrxpee = 1; |
214 | plat->safety_feat_cfg->mestee = 1; |
215 | plat->safety_feat_cfg->mrxee = 1; |
216 | plat->safety_feat_cfg->mtxee = 1; |
217 | plat->safety_feat_cfg->epsi = 1; |
218 | plat->safety_feat_cfg->edpp = 1; |
219 | plat->safety_feat_cfg->prtyen = 1; |
220 | plat->safety_feat_cfg->tmouten = 1; |
221 | |
222 | return stmmac_dvr_probe(device: &pdev->dev, plat_dat: plat, res: &res); |
223 | } |
224 | |
225 | /** |
226 | * stmmac_pci_remove |
227 | * |
228 | * @pdev: platform device pointer |
229 | * Description: this function calls the main to free the net resources |
230 | * and releases the PCI resources. |
231 | */ |
232 | static void stmmac_pci_remove(struct pci_dev *pdev) |
233 | { |
234 | int i; |
235 | |
236 | stmmac_dvr_remove(dev: &pdev->dev); |
237 | |
238 | for (i = 0; i < PCI_STD_NUM_BARS; i++) { |
239 | if (pci_resource_len(pdev, i) == 0) |
240 | continue; |
241 | pcim_iounmap_regions(pdev, BIT(i)); |
242 | break; |
243 | } |
244 | } |
245 | |
246 | static int __maybe_unused stmmac_pci_suspend(struct device *dev) |
247 | { |
248 | struct pci_dev *pdev = to_pci_dev(dev); |
249 | int ret; |
250 | |
251 | ret = stmmac_suspend(dev); |
252 | if (ret) |
253 | return ret; |
254 | |
255 | ret = pci_save_state(dev: pdev); |
256 | if (ret) |
257 | return ret; |
258 | |
259 | pci_disable_device(dev: pdev); |
260 | pci_wake_from_d3(dev: pdev, enable: true); |
261 | return 0; |
262 | } |
263 | |
264 | static int __maybe_unused stmmac_pci_resume(struct device *dev) |
265 | { |
266 | struct pci_dev *pdev = to_pci_dev(dev); |
267 | int ret; |
268 | |
269 | pci_restore_state(dev: pdev); |
270 | pci_set_power_state(dev: pdev, PCI_D0); |
271 | |
272 | ret = pci_enable_device(dev: pdev); |
273 | if (ret) |
274 | return ret; |
275 | |
276 | pci_set_master(dev: pdev); |
277 | |
278 | return stmmac_resume(dev); |
279 | } |
280 | |
281 | static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume); |
282 | |
283 | /* synthetic ID, no official vendor */ |
284 | #define PCI_VENDOR_ID_STMMAC 0x0700 |
285 | |
286 | #define PCI_DEVICE_ID_STMMAC_STMMAC 0x1108 |
287 | #define PCI_DEVICE_ID_SYNOPSYS_GMAC5_ID 0x7102 |
288 | |
289 | static const struct pci_device_id stmmac_id_table[] = { |
290 | { PCI_DEVICE_DATA(STMMAC, STMMAC, &stmmac_pci_info) }, |
291 | { PCI_DEVICE_DATA(STMICRO, MAC, &stmmac_pci_info) }, |
292 | { PCI_DEVICE_DATA(SYNOPSYS, GMAC5_ID, &snps_gmac5_pci_info) }, |
293 | {} |
294 | }; |
295 | |
296 | MODULE_DEVICE_TABLE(pci, stmmac_id_table); |
297 | |
298 | static struct pci_driver stmmac_pci_driver = { |
299 | .name = STMMAC_RESOURCE_NAME, |
300 | .id_table = stmmac_id_table, |
301 | .probe = stmmac_pci_probe, |
302 | .remove = stmmac_pci_remove, |
303 | .driver = { |
304 | .pm = &stmmac_pm_ops, |
305 | }, |
306 | }; |
307 | |
308 | module_pci_driver(stmmac_pci_driver); |
309 | |
310 | MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PCI driver" ); |
311 | MODULE_AUTHOR("Rayagond Kokatanur <rayagond.kokatanur@vayavyalabs.com>" ); |
312 | MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>" ); |
313 | MODULE_LICENSE("GPL" ); |
314 | |