Commit | Line | Data |
---|---|---|
a4d36e20 DV |
1 | /* |
2 | * aQuantia Corporation Network Driver | |
3 | * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | /* File aq_pci_func.c: Definition of PCI functions. */ | |
11 | ||
12 | #include "aq_pci_func.h" | |
13 | #include "aq_nic.h" | |
14 | #include "aq_vec.h" | |
15 | #include "aq_hw.h" | |
16 | #include <linux/interrupt.h> | |
17 | ||
18 | struct aq_pci_func_s { | |
19 | struct pci_dev *pdev; | |
20 | struct aq_nic_s *port[AQ_CFG_PCI_FUNC_PORTS]; | |
21 | void __iomem *mmio; | |
22 | void *aq_vec[AQ_CFG_PCI_FUNC_MSIX_IRQS]; | |
23 | resource_size_t mmio_pa; | |
24 | unsigned int msix_entry_mask; | |
a4d36e20 DV |
25 | unsigned int ports; |
26 | bool is_pci_enabled; | |
27 | bool is_regions; | |
28 | bool is_pci_using_dac; | |
29 | struct aq_hw_caps_s aq_hw_caps; | |
a4d36e20 DV |
30 | }; |
31 | ||
4cbc9f92 | 32 | struct aq_pci_func_s *aq_pci_func_alloc(const struct aq_hw_ops *aq_hw_ops, |
a4d36e20 DV |
33 | struct pci_dev *pdev, |
34 | const struct net_device_ops *ndev_ops, | |
35 | const struct ethtool_ops *eth_ops) | |
36 | { | |
37 | struct aq_pci_func_s *self = NULL; | |
38 | int err = 0; | |
39 | unsigned int port = 0U; | |
40 | ||
41 | if (!aq_hw_ops) { | |
42 | err = -EFAULT; | |
43 | goto err_exit; | |
44 | } | |
45 | self = kzalloc(sizeof(*self), GFP_KERNEL); | |
46 | if (!self) { | |
47 | err = -ENOMEM; | |
48 | goto err_exit; | |
49 | } | |
50 | ||
51 | pci_set_drvdata(pdev, self); | |
52 | self->pdev = pdev; | |
53 | ||
e4d02ca0 IR |
54 | err = aq_hw_ops->get_hw_caps(NULL, &self->aq_hw_caps, pdev->device, |
55 | pdev->subsystem_device); | |
a4d36e20 DV |
56 | if (err < 0) |
57 | goto err_exit; | |
58 | ||
59 | self->ports = self->aq_hw_caps.ports; | |
60 | ||
61 | for (port = 0; port < self->ports; ++port) { | |
62 | struct aq_nic_s *aq_nic = aq_nic_alloc_cold(ndev_ops, eth_ops, | |
e4d02ca0 | 63 | pdev, self, |
a4d36e20 DV |
64 | port, aq_hw_ops); |
65 | ||
66 | if (!aq_nic) { | |
67 | err = -ENOMEM; | |
68 | goto err_exit; | |
69 | } | |
70 | self->port[port] = aq_nic; | |
71 | } | |
72 | ||
73 | err_exit: | |
74 | if (err < 0) { | |
75 | if (self) | |
76 | aq_pci_func_free(self); | |
77 | self = NULL; | |
78 | } | |
79 | ||
80 | (void)err; | |
81 | return self; | |
82 | } | |
83 | ||
84 | int aq_pci_func_init(struct aq_pci_func_s *self) | |
85 | { | |
86 | int err = 0; | |
87 | unsigned int bar = 0U; | |
88 | unsigned int port = 0U; | |
4c8bb609 | 89 | unsigned int numvecs = 0U; |
a4d36e20 DV |
90 | |
91 | err = pci_enable_device(self->pdev); | |
92 | if (err < 0) | |
93 | goto err_exit; | |
94 | ||
95 | self->is_pci_enabled = true; | |
96 | ||
97 | err = pci_set_dma_mask(self->pdev, DMA_BIT_MASK(64)); | |
98 | if (!err) { | |
99 | err = pci_set_consistent_dma_mask(self->pdev, DMA_BIT_MASK(64)); | |
100 | self->is_pci_using_dac = 1; | |
101 | } | |
102 | if (err) { | |
103 | err = pci_set_dma_mask(self->pdev, DMA_BIT_MASK(32)); | |
104 | if (!err) | |
105 | err = pci_set_consistent_dma_mask(self->pdev, | |
106 | DMA_BIT_MASK(32)); | |
107 | self->is_pci_using_dac = 0; | |
108 | } | |
109 | if (err != 0) { | |
110 | err = -ENOSR; | |
111 | goto err_exit; | |
112 | } | |
113 | ||
114 | err = pci_request_regions(self->pdev, AQ_CFG_DRV_NAME "_mmio"); | |
115 | if (err < 0) | |
116 | goto err_exit; | |
117 | ||
118 | self->is_regions = true; | |
119 | ||
120 | pci_set_master(self->pdev); | |
121 | ||
122 | for (bar = 0; bar < 4; ++bar) { | |
123 | if (IORESOURCE_MEM & pci_resource_flags(self->pdev, bar)) { | |
124 | resource_size_t reg_sz; | |
125 | ||
126 | self->mmio_pa = pci_resource_start(self->pdev, bar); | |
127 | if (self->mmio_pa == 0U) { | |
128 | err = -EIO; | |
129 | goto err_exit; | |
130 | } | |
131 | ||
132 | reg_sz = pci_resource_len(self->pdev, bar); | |
133 | if ((reg_sz <= 24 /*ATL_REGS_SIZE*/)) { | |
134 | err = -EIO; | |
135 | goto err_exit; | |
136 | } | |
137 | ||
138 | self->mmio = ioremap_nocache(self->mmio_pa, reg_sz); | |
139 | if (!self->mmio) { | |
140 | err = -EIO; | |
141 | goto err_exit; | |
142 | } | |
143 | break; | |
144 | } | |
145 | } | |
146 | ||
4c8bb609 IR |
147 | numvecs = min((u8)AQ_CFG_VECS_DEF, self->aq_hw_caps.msix_irqs); |
148 | numvecs = min(numvecs, num_online_cpus()); | |
149 | ||
150 | /* enable interrupts */ | |
36a4a50f | 151 | #if !AQ_CFG_FORCE_LEGACY_INT |
4c8bb609 | 152 | err = pci_alloc_irq_vectors(self->pdev, numvecs, numvecs, PCI_IRQ_MSIX); |
a4d36e20 | 153 | |
36a4a50f CH |
154 | if (err < 0) { |
155 | err = pci_alloc_irq_vectors(self->pdev, 1, 1, | |
156 | PCI_IRQ_MSI | PCI_IRQ_LEGACY); | |
157 | if (err < 0) | |
158 | goto err_exit; | |
a4d36e20 | 159 | } |
4c8bb609 | 160 | #endif /* AQ_CFG_FORCE_LEGACY_INT */ |
a4d36e20 DV |
161 | |
162 | /* net device init */ | |
163 | for (port = 0; port < self->ports; ++port) { | |
164 | if (!self->port[port]) | |
165 | continue; | |
166 | ||
167 | err = aq_nic_cfg_start(self->port[port]); | |
168 | if (err < 0) | |
169 | goto err_exit; | |
170 | ||
171 | err = aq_nic_ndev_init(self->port[port]); | |
172 | if (err < 0) | |
173 | goto err_exit; | |
174 | ||
175 | err = aq_nic_ndev_register(self->port[port]); | |
176 | if (err < 0) | |
177 | goto err_exit; | |
178 | } | |
179 | ||
180 | err_exit: | |
181 | if (err < 0) | |
182 | aq_pci_func_deinit(self); | |
183 | return err; | |
184 | } | |
185 | ||
186 | int aq_pci_func_alloc_irq(struct aq_pci_func_s *self, unsigned int i, | |
187 | char *name, void *aq_vec, cpumask_t *affinity_mask) | |
188 | { | |
36a4a50f | 189 | struct pci_dev *pdev = self->pdev; |
a4d36e20 DV |
190 | int err = 0; |
191 | ||
36a4a50f CH |
192 | if (pdev->msix_enabled || pdev->msi_enabled) |
193 | err = request_irq(pci_irq_vector(pdev, i), aq_vec_isr, 0, | |
a4d36e20 | 194 | name, aq_vec); |
36a4a50f CH |
195 | else |
196 | err = request_irq(pci_irq_vector(pdev, i), aq_vec_isr_legacy, | |
a4d36e20 | 197 | IRQF_SHARED, name, aq_vec); |
a4d36e20 DV |
198 | |
199 | if (err >= 0) { | |
200 | self->msix_entry_mask |= (1 << i); | |
201 | self->aq_vec[i] = aq_vec; | |
202 | ||
36a4a50f CH |
203 | if (pdev->msix_enabled) |
204 | irq_set_affinity_hint(pci_irq_vector(pdev, i), | |
a4d36e20 DV |
205 | affinity_mask); |
206 | } | |
207 | ||
208 | return err; | |
209 | } | |
210 | ||
211 | void aq_pci_func_free_irqs(struct aq_pci_func_s *self) | |
212 | { | |
36a4a50f | 213 | struct pci_dev *pdev = self->pdev; |
a4d36e20 DV |
214 | unsigned int i = 0U; |
215 | ||
216 | for (i = 32U; i--;) { | |
217 | if (!((1U << i) & self->msix_entry_mask)) | |
218 | continue; | |
219 | ||
36a4a50f CH |
220 | if (pdev->msix_enabled) |
221 | irq_set_affinity_hint(pci_irq_vector(pdev, i), NULL); | |
ecd05225 | 222 | free_irq(pci_irq_vector(pdev, i), self->aq_vec[i]); |
a4d36e20 DV |
223 | self->msix_entry_mask &= ~(1U << i); |
224 | } | |
225 | } | |
226 | ||
227 | void __iomem *aq_pci_func_get_mmio(struct aq_pci_func_s *self) | |
228 | { | |
229 | return self->mmio; | |
230 | } | |
231 | ||
232 | unsigned int aq_pci_func_get_irq_type(struct aq_pci_func_s *self) | |
233 | { | |
36a4a50f CH |
234 | if (self->pdev->msix_enabled) |
235 | return AQ_HW_IRQ_MSIX; | |
236 | if (self->pdev->msi_enabled) | |
237 | return AQ_HW_IRQ_MSIX; | |
238 | return AQ_HW_IRQ_LEGACY; | |
a4d36e20 DV |
239 | } |
240 | ||
241 | void aq_pci_func_deinit(struct aq_pci_func_s *self) | |
242 | { | |
243 | if (!self) | |
244 | goto err_exit; | |
245 | ||
246 | aq_pci_func_free_irqs(self); | |
36a4a50f | 247 | pci_free_irq_vectors(self->pdev); |
a4d36e20 DV |
248 | |
249 | if (self->is_regions) | |
250 | pci_release_regions(self->pdev); | |
251 | ||
252 | if (self->is_pci_enabled) | |
253 | pci_disable_device(self->pdev); | |
254 | ||
255 | err_exit:; | |
256 | } | |
257 | ||
258 | void aq_pci_func_free(struct aq_pci_func_s *self) | |
259 | { | |
260 | unsigned int port = 0U; | |
261 | ||
262 | if (!self) | |
263 | goto err_exit; | |
264 | ||
265 | for (port = 0; port < self->ports; ++port) { | |
266 | if (!self->port[port]) | |
267 | continue; | |
268 | ||
269 | aq_nic_ndev_free(self->port[port]); | |
270 | } | |
271 | ||
6849540a IR |
272 | if (self->mmio) |
273 | iounmap(self->mmio); | |
274 | ||
a4d36e20 DV |
275 | kfree(self); |
276 | ||
277 | err_exit:; | |
278 | } | |
279 | ||
280 | int aq_pci_func_change_pm_state(struct aq_pci_func_s *self, | |
281 | pm_message_t *pm_msg) | |
282 | { | |
283 | int err = 0; | |
284 | unsigned int port = 0U; | |
285 | ||
286 | if (!self) { | |
287 | err = -EFAULT; | |
288 | goto err_exit; | |
289 | } | |
290 | for (port = 0; port < self->ports; ++port) { | |
291 | if (!self->port[port]) | |
292 | continue; | |
293 | ||
294 | (void)aq_nic_change_pm_state(self->port[port], pm_msg); | |
295 | } | |
296 | ||
297 | err_exit: | |
298 | return err; | |
299 | } |