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 | 10 | |
988b86e6 | 11 | #include <linux/compat.h> |
a755a45d | 12 | #include <linux/kernel.h> |
988b86e6 | 13 | #include <linux/miscdevice.h> |
a755a45d JG |
14 | #include <linux/slab.h> |
15 | #include <linux/err.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/pci.h> | |
988b86e6 | 18 | #include <linux/uaccess.h> |
a2ab8333 | 19 | #include <asm/pci_debug.h> |
a755a45d | 20 | #include <asm/pci_clp.h> |
988b86e6 MS |
21 | #include <asm/compat.h> |
22 | #include <asm/clp.h> | |
23 | #include <uapi/asm/clp.h> | |
a755a45d | 24 | |
1f1dcbd4 SO |
25 | static inline void zpci_err_clp(unsigned int rsp, int rc) |
26 | { | |
27 | struct { | |
28 | unsigned int rsp; | |
29 | int rc; | |
30 | } __packed data = {rsp, rc}; | |
31 | ||
32 | zpci_err_hex(&data, sizeof(data)); | |
33 | } | |
34 | ||
a755a45d | 35 | /* |
988b86e6 MS |
36 | * Call Logical Processor with c=1, lps=0 and command 1 |
37 | * to get the bit mask of installed logical processors | |
a755a45d | 38 | */ |
988b86e6 MS |
39 | static inline int clp_get_ilp(unsigned long *ilp) |
40 | { | |
41 | unsigned long mask; | |
42 | int cc = 3; | |
43 | ||
44 | asm volatile ( | |
45 | " .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n" | |
46 | "0: ipm %[cc]\n" | |
47 | " srl %[cc],28\n" | |
48 | "1:\n" | |
49 | EX_TABLE(0b, 1b) | |
50 | : [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1) | |
51 | : "cc"); | |
52 | *ilp = mask; | |
53 | return cc; | |
54 | } | |
55 | ||
56 | /* | |
57 | * Call Logical Processor with c=0, the give constant lps and an lpcb request. | |
58 | */ | |
59 | static inline int clp_req(void *data, unsigned int lps) | |
a755a45d | 60 | { |
bf4ec24f SO |
61 | struct { u8 _[CLP_BLK_SIZE]; } *req = data; |
62 | u64 ignored; | |
988b86e6 | 63 | int cc = 3; |
a755a45d JG |
64 | |
65 | asm volatile ( | |
988b86e6 MS |
66 | " .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n" |
67 | "0: ipm %[cc]\n" | |
a755a45d | 68 | " srl %[cc],28\n" |
988b86e6 MS |
69 | "1:\n" |
70 | EX_TABLE(0b, 1b) | |
71 | : [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req) | |
72 | : [req] "a" (req), [lps] "i" (lps) | |
bf4ec24f | 73 | : "cc"); |
a755a45d JG |
74 | return cc; |
75 | } | |
76 | ||
1d578966 | 77 | static void *clp_alloc_block(gfp_t gfp_mask) |
a755a45d | 78 | { |
1d578966 | 79 | return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE)); |
a755a45d JG |
80 | } |
81 | ||
82 | static void clp_free_block(void *ptr) | |
83 | { | |
84 | free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE)); | |
85 | } | |
86 | ||
87 | static void clp_store_query_pci_fngrp(struct zpci_dev *zdev, | |
88 | struct clp_rsp_query_pci_grp *response) | |
89 | { | |
828b35f6 JG |
90 | zdev->tlb_refresh = response->refresh; |
91 | zdev->dma_mask = response->dasm; | |
9a4da8a5 | 92 | zdev->msi_addr = response->msia; |
b19148f6 | 93 | zdev->max_msi = response->noi; |
d0b08853 | 94 | zdev->fmb_update = response->mui; |
9a4da8a5 | 95 | |
a755a45d JG |
96 | switch (response->version) { |
97 | case 1: | |
98 | zdev->max_bus_speed = PCIE_SPEED_5_0GT; | |
99 | break; | |
100 | default: | |
101 | zdev->max_bus_speed = PCI_SPEED_UNKNOWN; | |
102 | break; | |
103 | } | |
104 | } | |
105 | ||
106 | static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid) | |
107 | { | |
108 | struct clp_req_rsp_query_pci_grp *rrb; | |
109 | int rc; | |
110 | ||
1d578966 | 111 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
112 | if (!rrb) |
113 | return -ENOMEM; | |
114 | ||
115 | memset(rrb, 0, sizeof(*rrb)); | |
116 | rrb->request.hdr.len = sizeof(rrb->request); | |
117 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP; | |
118 | rrb->response.hdr.len = sizeof(rrb->response); | |
119 | rrb->request.pfgid = pfgid; | |
120 | ||
988b86e6 | 121 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
122 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) |
123 | clp_store_query_pci_fngrp(zdev, &rrb->response); | |
124 | else { | |
1f1dcbd4 SO |
125 | zpci_err("Q PCI FGRP:\n"); |
126 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
127 | rc = -EIO; |
128 | } | |
129 | clp_free_block(rrb); | |
130 | return rc; | |
131 | } | |
132 | ||
133 | static int clp_store_query_pci_fn(struct zpci_dev *zdev, | |
134 | struct clp_rsp_query_pci *response) | |
135 | { | |
136 | int i; | |
137 | ||
138 | for (i = 0; i < PCI_BAR_COUNT; i++) { | |
139 | zdev->bars[i].val = le32_to_cpu(response->bar[i]); | |
140 | zdev->bars[i].size = response->bar_size[i]; | |
141 | } | |
828b35f6 JG |
142 | zdev->start_dma = response->sdma; |
143 | zdev->end_dma = response->edma; | |
a755a45d JG |
144 | zdev->pchid = response->pchid; |
145 | zdev->pfgid = response->pfgid; | |
ac4995b9 SO |
146 | zdev->pft = response->pft; |
147 | zdev->vfn = response->vfn; | |
148 | zdev->uid = response->uid; | |
149 | ||
150 | memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip)); | |
151 | if (response->util_str_avail) { | |
152 | memcpy(zdev->util_str, response->util_str, | |
153 | sizeof(zdev->util_str)); | |
154 | } | |
155 | ||
a755a45d JG |
156 | return 0; |
157 | } | |
158 | ||
159 | static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh) | |
160 | { | |
161 | struct clp_req_rsp_query_pci *rrb; | |
162 | int rc; | |
163 | ||
1d578966 | 164 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
165 | if (!rrb) |
166 | return -ENOMEM; | |
167 | ||
168 | memset(rrb, 0, sizeof(*rrb)); | |
169 | rrb->request.hdr.len = sizeof(rrb->request); | |
170 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FN; | |
171 | rrb->response.hdr.len = sizeof(rrb->response); | |
172 | rrb->request.fh = fh; | |
173 | ||
988b86e6 | 174 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
175 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { |
176 | rc = clp_store_query_pci_fn(zdev, &rrb->response); | |
177 | if (rc) | |
178 | goto out; | |
179 | if (rrb->response.pfgid) | |
180 | rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid); | |
181 | } else { | |
1f1dcbd4 SO |
182 | zpci_err("Q PCI FN:\n"); |
183 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
184 | rc = -EIO; |
185 | } | |
186 | out: | |
187 | clp_free_block(rrb); | |
188 | return rc; | |
189 | } | |
190 | ||
191 | int clp_add_pci_device(u32 fid, u32 fh, int configured) | |
192 | { | |
193 | struct zpci_dev *zdev; | |
194 | int rc; | |
195 | ||
a2ab8333 | 196 | zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured); |
7d594322 SO |
197 | zdev = kzalloc(sizeof(*zdev), GFP_KERNEL); |
198 | if (!zdev) | |
199 | return -ENOMEM; | |
a755a45d JG |
200 | |
201 | zdev->fh = fh; | |
202 | zdev->fid = fid; | |
203 | ||
204 | /* Query function properties and update zdev */ | |
205 | rc = clp_query_pci_fn(zdev, fh); | |
206 | if (rc) | |
207 | goto error; | |
208 | ||
209 | if (configured) | |
210 | zdev->state = ZPCI_FN_STATE_CONFIGURED; | |
211 | else | |
212 | zdev->state = ZPCI_FN_STATE_STANDBY; | |
213 | ||
214 | rc = zpci_create_device(zdev); | |
215 | if (rc) | |
216 | goto error; | |
217 | return 0; | |
218 | ||
219 | error: | |
7d594322 | 220 | kfree(zdev); |
a755a45d JG |
221 | return rc; |
222 | } | |
223 | ||
224 | /* | |
225 | * Enable/Disable a given PCI function defined by its function handle. | |
226 | */ | |
227 | static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) | |
228 | { | |
229 | struct clp_req_rsp_set_pci *rrb; | |
d03abe58 | 230 | int rc, retries = 100; |
a755a45d | 231 | |
1d578966 | 232 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
233 | if (!rrb) |
234 | return -ENOMEM; | |
235 | ||
236 | do { | |
237 | memset(rrb, 0, sizeof(*rrb)); | |
238 | rrb->request.hdr.len = sizeof(rrb->request); | |
239 | rrb->request.hdr.cmd = CLP_SET_PCI_FN; | |
240 | rrb->response.hdr.len = sizeof(rrb->response); | |
241 | rrb->request.fh = *fh; | |
242 | rrb->request.oc = command; | |
243 | rrb->request.ndas = nr_dma_as; | |
244 | ||
988b86e6 | 245 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
246 | if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { |
247 | retries--; | |
248 | if (retries < 0) | |
249 | break; | |
d03abe58 | 250 | msleep(20); |
a755a45d JG |
251 | } |
252 | } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY); | |
253 | ||
254 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) | |
255 | *fh = rrb->response.fh; | |
256 | else { | |
1f1dcbd4 SO |
257 | zpci_err("Set PCI FN:\n"); |
258 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
259 | rc = -EIO; |
260 | } | |
261 | clp_free_block(rrb); | |
262 | return rc; | |
263 | } | |
264 | ||
265 | int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as) | |
266 | { | |
267 | u32 fh = zdev->fh; | |
268 | int rc; | |
269 | ||
270 | rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN); | |
271 | if (!rc) | |
272 | /* Success -> store enabled handle in zdev */ | |
273 | zdev->fh = fh; | |
a2ab8333 SO |
274 | |
275 | zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
276 | return rc; |
277 | } | |
278 | ||
279 | int clp_disable_fh(struct zpci_dev *zdev) | |
280 | { | |
281 | u32 fh = zdev->fh; | |
282 | int rc; | |
283 | ||
284 | if (!zdev_enabled(zdev)) | |
285 | return 0; | |
286 | ||
a755a45d JG |
287 | rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN); |
288 | if (!rc) | |
289 | /* Success -> store disabled handle in zdev */ | |
290 | zdev->fh = fh; | |
a2ab8333 SO |
291 | |
292 | zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
293 | return rc; |
294 | } | |
295 | ||
1d578966 SO |
296 | static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, |
297 | void (*cb)(struct clp_fh_list_entry *entry)) | |
a755a45d | 298 | { |
a755a45d JG |
299 | u64 resume_token = 0; |
300 | int entries, i, rc; | |
301 | ||
a755a45d JG |
302 | do { |
303 | memset(rrb, 0, sizeof(*rrb)); | |
304 | rrb->request.hdr.len = sizeof(rrb->request); | |
305 | rrb->request.hdr.cmd = CLP_LIST_PCI; | |
306 | /* store as many entries as possible */ | |
307 | rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN; | |
308 | rrb->request.resume_token = resume_token; | |
309 | ||
310 | /* Get PCI function handle list */ | |
988b86e6 | 311 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d | 312 | if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { |
1f1dcbd4 SO |
313 | zpci_err("List PCI FN:\n"); |
314 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
315 | rc = -EIO; |
316 | goto out; | |
317 | } | |
318 | ||
319 | WARN_ON_ONCE(rrb->response.entry_size != | |
320 | sizeof(struct clp_fh_list_entry)); | |
321 | ||
322 | entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) / | |
323 | rrb->response.entry_size; | |
a755a45d | 324 | |
a755a45d | 325 | resume_token = rrb->response.resume_token; |
a755a45d | 326 | for (i = 0; i < entries; i++) |
1d578966 | 327 | cb(&rrb->response.fh_list[i]); |
a755a45d | 328 | } while (resume_token); |
a755a45d | 329 | out: |
1d578966 SO |
330 | return rc; |
331 | } | |
332 | ||
333 | static void __clp_add(struct clp_fh_list_entry *entry) | |
334 | { | |
335 | if (!entry->vendor_id) | |
336 | return; | |
337 | ||
338 | clp_add_pci_device(entry->fid, entry->fh, entry->config_state); | |
339 | } | |
340 | ||
341 | static void __clp_rescan(struct clp_fh_list_entry *entry) | |
342 | { | |
343 | struct zpci_dev *zdev; | |
344 | ||
345 | if (!entry->vendor_id) | |
346 | return; | |
347 | ||
348 | zdev = get_zdev_by_fid(entry->fid); | |
349 | if (!zdev) { | |
350 | clp_add_pci_device(entry->fid, entry->fh, entry->config_state); | |
351 | return; | |
352 | } | |
353 | ||
354 | if (!entry->config_state) { | |
355 | /* | |
356 | * The handle is already disabled, that means no iota/irq freeing via | |
357 | * the firmware interfaces anymore. Need to free resources manually | |
358 | * (DMA memory, debug, sysfs)... | |
359 | */ | |
360 | zpci_stop_device(zdev); | |
361 | } | |
362 | } | |
363 | ||
57b5918c SO |
364 | static void __clp_update(struct clp_fh_list_entry *entry) |
365 | { | |
366 | struct zpci_dev *zdev; | |
367 | ||
368 | if (!entry->vendor_id) | |
369 | return; | |
370 | ||
371 | zdev = get_zdev_by_fid(entry->fid); | |
372 | if (!zdev) | |
373 | return; | |
374 | ||
375 | zdev->fh = entry->fh; | |
376 | } | |
377 | ||
1d578966 SO |
378 | int clp_scan_pci_devices(void) |
379 | { | |
380 | struct clp_req_rsp_list_pci *rrb; | |
381 | int rc; | |
382 | ||
383 | rrb = clp_alloc_block(GFP_KERNEL); | |
384 | if (!rrb) | |
385 | return -ENOMEM; | |
386 | ||
387 | rc = clp_list_pci(rrb, __clp_add); | |
388 | ||
389 | clp_free_block(rrb); | |
390 | return rc; | |
391 | } | |
392 | ||
393 | int clp_rescan_pci_devices(void) | |
394 | { | |
395 | struct clp_req_rsp_list_pci *rrb; | |
396 | int rc; | |
397 | ||
398 | rrb = clp_alloc_block(GFP_KERNEL); | |
399 | if (!rrb) | |
400 | return -ENOMEM; | |
401 | ||
402 | rc = clp_list_pci(rrb, __clp_rescan); | |
403 | ||
a755a45d JG |
404 | clp_free_block(rrb); |
405 | return rc; | |
406 | } | |
57b5918c SO |
407 | |
408 | int clp_rescan_pci_devices_simple(void) | |
409 | { | |
410 | struct clp_req_rsp_list_pci *rrb; | |
411 | int rc; | |
412 | ||
413 | rrb = clp_alloc_block(GFP_NOWAIT); | |
414 | if (!rrb) | |
415 | return -ENOMEM; | |
416 | ||
417 | rc = clp_list_pci(rrb, __clp_update); | |
418 | ||
419 | clp_free_block(rrb); | |
420 | return rc; | |
421 | } | |
988b86e6 MS |
422 | |
423 | static int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | |
424 | { | |
425 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
426 | ||
427 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
428 | lpcb->response.hdr.len > limit) | |
429 | return -EINVAL; | |
430 | return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0; | |
431 | } | |
432 | ||
433 | static int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb) | |
434 | { | |
435 | switch (lpcb->cmd) { | |
436 | case 0x0001: /* store logical-processor characteristics */ | |
437 | return clp_base_slpc(req, (void *) lpcb); | |
438 | default: | |
439 | return -EINVAL; | |
440 | } | |
441 | } | |
442 | ||
443 | static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | |
444 | { | |
445 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
446 | ||
447 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
448 | lpcb->response.hdr.len > limit) | |
449 | return -EINVAL; | |
450 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
451 | } | |
452 | ||
453 | static int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb) | |
454 | { | |
455 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
456 | ||
457 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
458 | lpcb->response.hdr.len > limit) | |
459 | return -EINVAL; | |
460 | if (lpcb->request.reserved2 != 0) | |
461 | return -EINVAL; | |
462 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
463 | } | |
464 | ||
465 | static int clp_pci_query(struct clp_req *req, | |
466 | struct clp_req_rsp_query_pci *lpcb) | |
467 | { | |
468 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
469 | ||
470 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
471 | lpcb->response.hdr.len > limit) | |
472 | return -EINVAL; | |
473 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0) | |
474 | return -EINVAL; | |
475 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
476 | } | |
477 | ||
478 | static int clp_pci_query_grp(struct clp_req *req, | |
479 | struct clp_req_rsp_query_pci_grp *lpcb) | |
480 | { | |
481 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
482 | ||
483 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
484 | lpcb->response.hdr.len > limit) | |
485 | return -EINVAL; | |
486 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 || | |
487 | lpcb->request.reserved4 != 0) | |
488 | return -EINVAL; | |
489 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
490 | } | |
491 | ||
492 | static int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb) | |
493 | { | |
494 | switch (lpcb->cmd) { | |
495 | case 0x0001: /* store logical-processor characteristics */ | |
496 | return clp_pci_slpc(req, (void *) lpcb); | |
497 | case 0x0002: /* list PCI functions */ | |
498 | return clp_pci_list(req, (void *) lpcb); | |
499 | case 0x0003: /* query PCI function */ | |
500 | return clp_pci_query(req, (void *) lpcb); | |
501 | case 0x0004: /* query PCI function group */ | |
502 | return clp_pci_query_grp(req, (void *) lpcb); | |
503 | default: | |
504 | return -EINVAL; | |
505 | } | |
506 | } | |
507 | ||
508 | static int clp_normal_command(struct clp_req *req) | |
509 | { | |
510 | struct clp_req_hdr *lpcb; | |
511 | void __user *uptr; | |
512 | int rc; | |
513 | ||
514 | rc = -EINVAL; | |
515 | if (req->lps != 0 && req->lps != 2) | |
516 | goto out; | |
517 | ||
518 | rc = -ENOMEM; | |
519 | lpcb = clp_alloc_block(GFP_KERNEL); | |
520 | if (!lpcb) | |
521 | goto out; | |
522 | ||
523 | rc = -EFAULT; | |
524 | uptr = (void __force __user *)(unsigned long) req->data_p; | |
525 | if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0) | |
526 | goto out_free; | |
527 | ||
528 | rc = -EINVAL; | |
529 | if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0) | |
530 | goto out_free; | |
531 | ||
532 | switch (req->lps) { | |
533 | case 0: | |
534 | rc = clp_base_command(req, lpcb); | |
535 | break; | |
536 | case 2: | |
537 | rc = clp_pci_command(req, lpcb); | |
538 | break; | |
539 | } | |
540 | if (rc) | |
541 | goto out_free; | |
542 | ||
543 | rc = -EFAULT; | |
544 | if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0) | |
545 | goto out_free; | |
546 | ||
547 | rc = 0; | |
548 | ||
549 | out_free: | |
550 | clp_free_block(lpcb); | |
551 | out: | |
552 | return rc; | |
553 | } | |
554 | ||
555 | static int clp_immediate_command(struct clp_req *req) | |
556 | { | |
557 | void __user *uptr; | |
558 | unsigned long ilp; | |
559 | int exists; | |
560 | ||
561 | if (req->cmd > 1 || clp_get_ilp(&ilp) != 0) | |
562 | return -EINVAL; | |
563 | ||
564 | uptr = (void __force __user *)(unsigned long) req->data_p; | |
565 | if (req->cmd == 0) { | |
566 | /* Command code 0: test for a specific processor */ | |
567 | exists = test_bit_inv(req->lps, &ilp); | |
568 | return put_user(exists, (int __user *) uptr); | |
569 | } | |
570 | /* Command code 1: return bit mask of installed processors */ | |
571 | return put_user(ilp, (unsigned long __user *) uptr); | |
572 | } | |
573 | ||
574 | static long clp_misc_ioctl(struct file *filp, unsigned int cmd, | |
575 | unsigned long arg) | |
576 | { | |
577 | struct clp_req req; | |
578 | void __user *argp; | |
579 | ||
580 | if (cmd != CLP_SYNC) | |
581 | return -EINVAL; | |
582 | ||
583 | argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg; | |
584 | if (copy_from_user(&req, argp, sizeof(req))) | |
585 | return -EFAULT; | |
586 | if (req.r != 0) | |
587 | return -EINVAL; | |
588 | return req.c ? clp_immediate_command(&req) : clp_normal_command(&req); | |
589 | } | |
590 | ||
591 | static int clp_misc_release(struct inode *inode, struct file *filp) | |
592 | { | |
593 | return 0; | |
594 | } | |
595 | ||
596 | static const struct file_operations clp_misc_fops = { | |
597 | .owner = THIS_MODULE, | |
598 | .open = nonseekable_open, | |
599 | .release = clp_misc_release, | |
600 | .unlocked_ioctl = clp_misc_ioctl, | |
601 | .compat_ioctl = clp_misc_ioctl, | |
602 | .llseek = no_llseek, | |
603 | }; | |
604 | ||
605 | static struct miscdevice clp_misc_device = { | |
606 | .minor = MISC_DYNAMIC_MINOR, | |
607 | .name = "clp", | |
608 | .fops = &clp_misc_fops, | |
609 | }; | |
610 | ||
611 | static int __init clp_misc_init(void) | |
612 | { | |
613 | return misc_register(&clp_misc_device); | |
614 | } | |
615 | ||
616 | device_initcall(clp_misc_init); |