diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/pci-bridge.h | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_dn.c | 116 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 16 |
3 files changed, 135 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 2c6dc2a3d14a..ece30f589398 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h | |||
@@ -156,6 +156,7 @@ struct iommu_table; | |||
156 | 156 | ||
157 | struct pci_dn { | 157 | struct pci_dn { |
158 | int flags; | 158 | int flags; |
159 | #define PCI_DN_FLAG_IOV_VF 0x01 | ||
159 | 160 | ||
160 | int busno; /* pci bus number */ | 161 | int busno; /* pci bus number */ |
161 | int devfn; /* pci device and function number */ | 162 | int devfn; /* pci device and function number */ |
@@ -188,6 +189,8 @@ struct pci_dn { | |||
188 | extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, | 189 | extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, |
189 | int devfn); | 190 | int devfn); |
190 | extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); | 191 | extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); |
192 | extern struct pci_dn *add_dev_pci_data(struct pci_dev *pdev); | ||
193 | extern void remove_dev_pci_data(struct pci_dev *pdev); | ||
191 | extern void *update_dn_pci_info(struct device_node *dn, void *data); | 194 | extern void *update_dn_pci_info(struct device_node *dn, void *data); |
192 | 195 | ||
193 | static inline int pci_device_from_OF_node(struct device_node *np, | 196 | static inline int pci_device_from_OF_node(struct device_node *np, |
diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 65b98367005c..e5f1d78ef7cf 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c | |||
@@ -136,6 +136,122 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev) | |||
136 | return NULL; | 136 | return NULL; |
137 | } | 137 | } |
138 | 138 | ||
139 | #ifdef CONFIG_PCI_IOV | ||
140 | static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent, | ||
141 | struct pci_dev *pdev, | ||
142 | int busno, int devfn) | ||
143 | { | ||
144 | struct pci_dn *pdn; | ||
145 | |||
146 | /* Except PHB, we always have the parent */ | ||
147 | if (!parent) | ||
148 | return NULL; | ||
149 | |||
150 | pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); | ||
151 | if (!pdn) { | ||
152 | dev_warn(&pdev->dev, "%s: Out of memory!\n", __func__); | ||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | pdn->phb = parent->phb; | ||
157 | pdn->parent = parent; | ||
158 | pdn->busno = busno; | ||
159 | pdn->devfn = devfn; | ||
160 | #ifdef CONFIG_PPC_POWERNV | ||
161 | pdn->pe_number = IODA_INVALID_PE; | ||
162 | #endif | ||
163 | INIT_LIST_HEAD(&pdn->child_list); | ||
164 | INIT_LIST_HEAD(&pdn->list); | ||
165 | list_add_tail(&pdn->list, &parent->child_list); | ||
166 | |||
167 | /* | ||
168 | * If we already have PCI device instance, lets | ||
169 | * bind them. | ||
170 | */ | ||
171 | if (pdev) | ||
172 | pdev->dev.archdata.pci_data = pdn; | ||
173 | |||
174 | return pdn; | ||
175 | } | ||
176 | #endif | ||
177 | |||
178 | struct pci_dn *add_dev_pci_data(struct pci_dev *pdev) | ||
179 | { | ||
180 | #ifdef CONFIG_PCI_IOV | ||
181 | struct pci_dn *parent, *pdn; | ||
182 | int i; | ||
183 | |||
184 | /* Only support IOV for now */ | ||
185 | if (!pdev->is_physfn) | ||
186 | return pci_get_pdn(pdev); | ||
187 | |||
188 | /* Check if VFs have been populated */ | ||
189 | pdn = pci_get_pdn(pdev); | ||
190 | if (!pdn || (pdn->flags & PCI_DN_FLAG_IOV_VF)) | ||
191 | return NULL; | ||
192 | |||
193 | pdn->flags |= PCI_DN_FLAG_IOV_VF; | ||
194 | parent = pci_bus_to_pdn(pdev->bus); | ||
195 | if (!parent) | ||
196 | return NULL; | ||
197 | |||
198 | for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { | ||
199 | pdn = add_one_dev_pci_data(parent, NULL, | ||
200 | pci_iov_virtfn_bus(pdev, i), | ||
201 | pci_iov_virtfn_devfn(pdev, i)); | ||
202 | if (!pdn) { | ||
203 | dev_warn(&pdev->dev, "%s: Cannot create firmware data for VF#%d\n", | ||
204 | __func__, i); | ||
205 | return NULL; | ||
206 | } | ||
207 | } | ||
208 | #endif /* CONFIG_PCI_IOV */ | ||
209 | |||
210 | return pci_get_pdn(pdev); | ||
211 | } | ||
212 | |||
213 | void remove_dev_pci_data(struct pci_dev *pdev) | ||
214 | { | ||
215 | #ifdef CONFIG_PCI_IOV | ||
216 | struct pci_dn *parent; | ||
217 | struct pci_dn *pdn, *tmp; | ||
218 | int i; | ||
219 | |||
220 | /* Only support IOV PF for now */ | ||
221 | if (!pdev->is_physfn) | ||
222 | return; | ||
223 | |||
224 | /* Check if VFs have been populated */ | ||
225 | pdn = pci_get_pdn(pdev); | ||
226 | if (!pdn || !(pdn->flags & PCI_DN_FLAG_IOV_VF)) | ||
227 | return; | ||
228 | |||
229 | pdn->flags &= ~PCI_DN_FLAG_IOV_VF; | ||
230 | parent = pci_bus_to_pdn(pdev->bus); | ||
231 | if (!parent) | ||
232 | return; | ||
233 | |||
234 | /* | ||
235 | * We might introduce flag to pci_dn in future | ||
236 | * so that we can release VF's firmware data in | ||
237 | * a batch mode. | ||
238 | */ | ||
239 | for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { | ||
240 | list_for_each_entry_safe(pdn, tmp, | ||
241 | &parent->child_list, list) { | ||
242 | if (pdn->busno != pci_iov_virtfn_bus(pdev, i) || | ||
243 | pdn->devfn != pci_iov_virtfn_devfn(pdev, i)) | ||
244 | continue; | ||
245 | |||
246 | if (!list_empty(&pdn->list)) | ||
247 | list_del(&pdn->list); | ||
248 | |||
249 | kfree(pdn); | ||
250 | } | ||
251 | } | ||
252 | #endif /* CONFIG_PCI_IOV */ | ||
253 | } | ||
254 | |||
139 | /* | 255 | /* |
140 | * Traverse_func that inits the PCI fields of the device node. | 256 | * Traverse_func that inits the PCI fields of the device node. |
141 | * NOTE: this *must* be done before read/write config to the device. | 257 | * NOTE: this *must* be done before read/write config to the device. |
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 26fe09936935..7f58f199f2c1 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -974,6 +974,22 @@ static void pnv_pci_ioda_setup_PEs(void) | |||
974 | } | 974 | } |
975 | } | 975 | } |
976 | 976 | ||
977 | #ifdef CONFIG_PCI_IOV | ||
978 | int pcibios_sriov_disable(struct pci_dev *pdev) | ||
979 | { | ||
980 | /* Release PCI data */ | ||
981 | remove_dev_pci_data(pdev); | ||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) | ||
986 | { | ||
987 | /* Allocate PCI data */ | ||
988 | add_dev_pci_data(pdev); | ||
989 | return 0; | ||
990 | } | ||
991 | #endif /* CONFIG_PCI_IOV */ | ||
992 | |||
977 | static void pnv_pci_ioda_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev) | 993 | static void pnv_pci_ioda_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev) |
978 | { | 994 | { |
979 | struct pci_dn *pdn = pci_get_pdn(pdev); | 995 | struct pci_dn *pdn = pci_get_pdn(pdev); |