Commit | Line | Data |
---|---|---|
a755a45d JG |
1 | /* |
2 | * Copyright IBM Corp. 2012 | |
3 | * | |
4 | * Author(s): | |
5 | * Jan Glauber <jang@linux.vnet.ibm.com> | |
6 | */ | |
7 | ||
896cb7e6 GS |
8 | #define KMSG_COMPONENT "zpci" |
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
a755a45d JG |
10 | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/pci.h> | |
a2ab8333 | 16 | #include <asm/pci_debug.h> |
a755a45d JG |
17 | #include <asm/pci_clp.h> |
18 | ||
1f1dcbd4 SO |
19 | static inline void zpci_err_clp(unsigned int rsp, int rc) |
20 | { | |
21 | struct { | |
22 | unsigned int rsp; | |
23 | int rc; | |
24 | } __packed data = {rsp, rc}; | |
25 | ||
26 | zpci_err_hex(&data, sizeof(data)); | |
27 | } | |
28 | ||
a755a45d JG |
29 | /* |
30 | * Call Logical Processor | |
31 | * Retry logic is handled by the caller. | |
32 | */ | |
bf4ec24f | 33 | static inline u8 clp_instr(void *data) |
a755a45d | 34 | { |
bf4ec24f SO |
35 | struct { u8 _[CLP_BLK_SIZE]; } *req = data; |
36 | u64 ignored; | |
a755a45d JG |
37 | u8 cc; |
38 | ||
39 | asm volatile ( | |
bf4ec24f | 40 | " .insn rrf,0xb9a00000,%[ign],%[req],0x0,0x2\n" |
a755a45d JG |
41 | " ipm %[cc]\n" |
42 | " srl %[cc],28\n" | |
bf4ec24f | 43 | : [cc] "=d" (cc), [ign] "=d" (ignored), "+m" (*req) |
a755a45d | 44 | : [req] "a" (req) |
bf4ec24f | 45 | : "cc"); |
a755a45d JG |
46 | return cc; |
47 | } | |
48 | ||
1d578966 | 49 | static void *clp_alloc_block(gfp_t gfp_mask) |
a755a45d | 50 | { |
1d578966 | 51 | return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE)); |
a755a45d JG |
52 | } |
53 | ||
54 | static void clp_free_block(void *ptr) | |
55 | { | |
56 | free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE)); | |
57 | } | |
58 | ||
59 | static void clp_store_query_pci_fngrp(struct zpci_dev *zdev, | |
60 | struct clp_rsp_query_pci_grp *response) | |
61 | { | |
828b35f6 JG |
62 | zdev->tlb_refresh = response->refresh; |
63 | zdev->dma_mask = response->dasm; | |
9a4da8a5 | 64 | zdev->msi_addr = response->msia; |
d0b08853 | 65 | zdev->fmb_update = response->mui; |
9a4da8a5 | 66 | |
a755a45d JG |
67 | switch (response->version) { |
68 | case 1: | |
69 | zdev->max_bus_speed = PCIE_SPEED_5_0GT; | |
70 | break; | |
71 | default: | |
72 | zdev->max_bus_speed = PCI_SPEED_UNKNOWN; | |
73 | break; | |
74 | } | |
75 | } | |
76 | ||
77 | static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid) | |
78 | { | |
79 | struct clp_req_rsp_query_pci_grp *rrb; | |
80 | int rc; | |
81 | ||
1d578966 | 82 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
83 | if (!rrb) |
84 | return -ENOMEM; | |
85 | ||
86 | memset(rrb, 0, sizeof(*rrb)); | |
87 | rrb->request.hdr.len = sizeof(rrb->request); | |
88 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP; | |
89 | rrb->response.hdr.len = sizeof(rrb->response); | |
90 | rrb->request.pfgid = pfgid; | |
91 | ||
92 | rc = clp_instr(rrb); | |
93 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) | |
94 | clp_store_query_pci_fngrp(zdev, &rrb->response); | |
95 | else { | |
1f1dcbd4 SO |
96 | zpci_err("Q PCI FGRP:\n"); |
97 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
98 | rc = -EIO; |
99 | } | |
100 | clp_free_block(rrb); | |
101 | return rc; | |
102 | } | |
103 | ||
104 | static int clp_store_query_pci_fn(struct zpci_dev *zdev, | |
105 | struct clp_rsp_query_pci *response) | |
106 | { | |
107 | int i; | |
108 | ||
109 | for (i = 0; i < PCI_BAR_COUNT; i++) { | |
110 | zdev->bars[i].val = le32_to_cpu(response->bar[i]); | |
111 | zdev->bars[i].size = response->bar_size[i]; | |
112 | } | |
828b35f6 JG |
113 | zdev->start_dma = response->sdma; |
114 | zdev->end_dma = response->edma; | |
a755a45d JG |
115 | zdev->pchid = response->pchid; |
116 | zdev->pfgid = response->pfgid; | |
ac4995b9 SO |
117 | zdev->pft = response->pft; |
118 | zdev->vfn = response->vfn; | |
119 | zdev->uid = response->uid; | |
120 | ||
121 | memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip)); | |
122 | if (response->util_str_avail) { | |
123 | memcpy(zdev->util_str, response->util_str, | |
124 | sizeof(zdev->util_str)); | |
125 | } | |
126 | ||
a755a45d JG |
127 | return 0; |
128 | } | |
129 | ||
130 | static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh) | |
131 | { | |
132 | struct clp_req_rsp_query_pci *rrb; | |
133 | int rc; | |
134 | ||
1d578966 | 135 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
136 | if (!rrb) |
137 | return -ENOMEM; | |
138 | ||
139 | memset(rrb, 0, sizeof(*rrb)); | |
140 | rrb->request.hdr.len = sizeof(rrb->request); | |
141 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FN; | |
142 | rrb->response.hdr.len = sizeof(rrb->response); | |
143 | rrb->request.fh = fh; | |
144 | ||
145 | rc = clp_instr(rrb); | |
146 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { | |
147 | rc = clp_store_query_pci_fn(zdev, &rrb->response); | |
148 | if (rc) | |
149 | goto out; | |
150 | if (rrb->response.pfgid) | |
151 | rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid); | |
152 | } else { | |
1f1dcbd4 SO |
153 | zpci_err("Q PCI FN:\n"); |
154 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
155 | rc = -EIO; |
156 | } | |
157 | out: | |
158 | clp_free_block(rrb); | |
159 | return rc; | |
160 | } | |
161 | ||
162 | int clp_add_pci_device(u32 fid, u32 fh, int configured) | |
163 | { | |
164 | struct zpci_dev *zdev; | |
165 | int rc; | |
166 | ||
a2ab8333 | 167 | zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured); |
7d594322 SO |
168 | zdev = kzalloc(sizeof(*zdev), GFP_KERNEL); |
169 | if (!zdev) | |
170 | return -ENOMEM; | |
a755a45d JG |
171 | |
172 | zdev->fh = fh; | |
173 | zdev->fid = fid; | |
174 | ||
175 | /* Query function properties and update zdev */ | |
176 | rc = clp_query_pci_fn(zdev, fh); | |
177 | if (rc) | |
178 | goto error; | |
179 | ||
180 | if (configured) | |
181 | zdev->state = ZPCI_FN_STATE_CONFIGURED; | |
182 | else | |
183 | zdev->state = ZPCI_FN_STATE_STANDBY; | |
184 | ||
185 | rc = zpci_create_device(zdev); | |
186 | if (rc) | |
187 | goto error; | |
188 | return 0; | |
189 | ||
190 | error: | |
7d594322 | 191 | kfree(zdev); |
a755a45d JG |
192 | return rc; |
193 | } | |
194 | ||
195 | /* | |
196 | * Enable/Disable a given PCI function defined by its function handle. | |
197 | */ | |
198 | static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) | |
199 | { | |
200 | struct clp_req_rsp_set_pci *rrb; | |
d03abe58 | 201 | int rc, retries = 100; |
a755a45d | 202 | |
1d578966 | 203 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
204 | if (!rrb) |
205 | return -ENOMEM; | |
206 | ||
207 | do { | |
208 | memset(rrb, 0, sizeof(*rrb)); | |
209 | rrb->request.hdr.len = sizeof(rrb->request); | |
210 | rrb->request.hdr.cmd = CLP_SET_PCI_FN; | |
211 | rrb->response.hdr.len = sizeof(rrb->response); | |
212 | rrb->request.fh = *fh; | |
213 | rrb->request.oc = command; | |
214 | rrb->request.ndas = nr_dma_as; | |
215 | ||
216 | rc = clp_instr(rrb); | |
217 | if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { | |
218 | retries--; | |
219 | if (retries < 0) | |
220 | break; | |
d03abe58 | 221 | msleep(20); |
a755a45d JG |
222 | } |
223 | } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY); | |
224 | ||
225 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) | |
226 | *fh = rrb->response.fh; | |
227 | else { | |
1f1dcbd4 SO |
228 | zpci_err("Set PCI FN:\n"); |
229 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
230 | rc = -EIO; |
231 | } | |
232 | clp_free_block(rrb); | |
233 | return rc; | |
234 | } | |
235 | ||
236 | int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as) | |
237 | { | |
238 | u32 fh = zdev->fh; | |
239 | int rc; | |
240 | ||
241 | rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN); | |
242 | if (!rc) | |
243 | /* Success -> store enabled handle in zdev */ | |
244 | zdev->fh = fh; | |
a2ab8333 SO |
245 | |
246 | zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
247 | return rc; |
248 | } | |
249 | ||
250 | int clp_disable_fh(struct zpci_dev *zdev) | |
251 | { | |
252 | u32 fh = zdev->fh; | |
253 | int rc; | |
254 | ||
255 | if (!zdev_enabled(zdev)) | |
256 | return 0; | |
257 | ||
a755a45d JG |
258 | rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN); |
259 | if (!rc) | |
260 | /* Success -> store disabled handle in zdev */ | |
261 | zdev->fh = fh; | |
a2ab8333 SO |
262 | |
263 | zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
264 | return rc; |
265 | } | |
266 | ||
1d578966 SO |
267 | static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, |
268 | void (*cb)(struct clp_fh_list_entry *entry)) | |
a755a45d | 269 | { |
a755a45d JG |
270 | u64 resume_token = 0; |
271 | int entries, i, rc; | |
272 | ||
a755a45d JG |
273 | do { |
274 | memset(rrb, 0, sizeof(*rrb)); | |
275 | rrb->request.hdr.len = sizeof(rrb->request); | |
276 | rrb->request.hdr.cmd = CLP_LIST_PCI; | |
277 | /* store as many entries as possible */ | |
278 | rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN; | |
279 | rrb->request.resume_token = resume_token; | |
280 | ||
281 | /* Get PCI function handle list */ | |
282 | rc = clp_instr(rrb); | |
283 | if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { | |
1f1dcbd4 SO |
284 | zpci_err("List PCI FN:\n"); |
285 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
286 | rc = -EIO; |
287 | goto out; | |
288 | } | |
289 | ||
290 | WARN_ON_ONCE(rrb->response.entry_size != | |
291 | sizeof(struct clp_fh_list_entry)); | |
292 | ||
293 | entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) / | |
294 | rrb->response.entry_size; | |
a755a45d | 295 | |
a755a45d | 296 | resume_token = rrb->response.resume_token; |
a755a45d | 297 | for (i = 0; i < entries; i++) |
1d578966 | 298 | cb(&rrb->response.fh_list[i]); |
a755a45d | 299 | } while (resume_token); |
a755a45d | 300 | out: |
1d578966 SO |
301 | return rc; |
302 | } | |
303 | ||
304 | static void __clp_add(struct clp_fh_list_entry *entry) | |
305 | { | |
306 | if (!entry->vendor_id) | |
307 | return; | |
308 | ||
309 | clp_add_pci_device(entry->fid, entry->fh, entry->config_state); | |
310 | } | |
311 | ||
312 | static void __clp_rescan(struct clp_fh_list_entry *entry) | |
313 | { | |
314 | struct zpci_dev *zdev; | |
315 | ||
316 | if (!entry->vendor_id) | |
317 | return; | |
318 | ||
319 | zdev = get_zdev_by_fid(entry->fid); | |
320 | if (!zdev) { | |
321 | clp_add_pci_device(entry->fid, entry->fh, entry->config_state); | |
322 | return; | |
323 | } | |
324 | ||
325 | if (!entry->config_state) { | |
326 | /* | |
327 | * The handle is already disabled, that means no iota/irq freeing via | |
328 | * the firmware interfaces anymore. Need to free resources manually | |
329 | * (DMA memory, debug, sysfs)... | |
330 | */ | |
331 | zpci_stop_device(zdev); | |
332 | } | |
333 | } | |
334 | ||
57b5918c SO |
335 | static void __clp_update(struct clp_fh_list_entry *entry) |
336 | { | |
337 | struct zpci_dev *zdev; | |
338 | ||
339 | if (!entry->vendor_id) | |
340 | return; | |
341 | ||
342 | zdev = get_zdev_by_fid(entry->fid); | |
343 | if (!zdev) | |
344 | return; | |
345 | ||
346 | zdev->fh = entry->fh; | |
347 | } | |
348 | ||
1d578966 SO |
349 | int clp_scan_pci_devices(void) |
350 | { | |
351 | struct clp_req_rsp_list_pci *rrb; | |
352 | int rc; | |
353 | ||
354 | rrb = clp_alloc_block(GFP_KERNEL); | |
355 | if (!rrb) | |
356 | return -ENOMEM; | |
357 | ||
358 | rc = clp_list_pci(rrb, __clp_add); | |
359 | ||
360 | clp_free_block(rrb); | |
361 | return rc; | |
362 | } | |
363 | ||
364 | int clp_rescan_pci_devices(void) | |
365 | { | |
366 | struct clp_req_rsp_list_pci *rrb; | |
367 | int rc; | |
368 | ||
369 | rrb = clp_alloc_block(GFP_KERNEL); | |
370 | if (!rrb) | |
371 | return -ENOMEM; | |
372 | ||
373 | rc = clp_list_pci(rrb, __clp_rescan); | |
374 | ||
a755a45d JG |
375 | clp_free_block(rrb); |
376 | return rc; | |
377 | } | |
57b5918c SO |
378 | |
379 | int clp_rescan_pci_devices_simple(void) | |
380 | { | |
381 | struct clp_req_rsp_list_pci *rrb; | |
382 | int rc; | |
383 | ||
384 | rrb = clp_alloc_block(GFP_NOWAIT); | |
385 | if (!rrb) | |
386 | return -ENOMEM; | |
387 | ||
388 | rc = clp_list_pci(rrb, __clp_update); | |
389 | ||
390 | clp_free_block(rrb); | |
391 | return rc; | |
392 | } |