]>
Commit | Line | Data |
---|---|---|
76ebe01f UK |
1 | /* |
2 | * CXL Flash Device Driver | |
3 | * | |
4 | * Written by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation | |
5 | * Uma Krishnan <ukrishn@linux.vnet.ibm.com>, IBM Corporation | |
6 | * | |
7 | * Copyright (C) 2018 IBM Corporation | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; either version | |
12 | * 2 of the License, or (at your option) any later version. | |
13 | */ | |
14 | ||
926a62f9 | 15 | #include <linux/file.h> |
429ebfa6 | 16 | #include <linux/idr.h> |
926a62f9 UK |
17 | #include <linux/module.h> |
18 | #include <linux/mount.h> | |
429ebfa6 | 19 | |
76ebe01f UK |
20 | #include <misc/ocxl.h> |
21 | ||
22 | #include "backend.h" | |
48e077db UK |
23 | #include "ocxl_hw.h" |
24 | ||
926a62f9 UK |
25 | /* |
26 | * Pseudo-filesystem to allocate inodes. | |
27 | */ | |
28 | ||
29 | #define OCXLFLASH_FS_MAGIC 0x1697698f | |
30 | ||
31 | static int ocxlflash_fs_cnt; | |
32 | static struct vfsmount *ocxlflash_vfs_mount; | |
33 | ||
34 | static const struct dentry_operations ocxlflash_fs_dops = { | |
35 | .d_dname = simple_dname, | |
36 | }; | |
37 | ||
38 | /* | |
39 | * ocxlflash_fs_mount() - mount the pseudo-filesystem | |
40 | * @fs_type: File system type. | |
41 | * @flags: Flags for the filesystem. | |
42 | * @dev_name: Device name associated with the filesystem. | |
43 | * @data: Data pointer. | |
44 | * | |
45 | * Return: pointer to the directory entry structure | |
46 | */ | |
47 | static struct dentry *ocxlflash_fs_mount(struct file_system_type *fs_type, | |
48 | int flags, const char *dev_name, | |
49 | void *data) | |
50 | { | |
51 | return mount_pseudo(fs_type, "ocxlflash:", NULL, &ocxlflash_fs_dops, | |
52 | OCXLFLASH_FS_MAGIC); | |
53 | } | |
54 | ||
55 | static struct file_system_type ocxlflash_fs_type = { | |
56 | .name = "ocxlflash", | |
57 | .owner = THIS_MODULE, | |
58 | .mount = ocxlflash_fs_mount, | |
59 | .kill_sb = kill_anon_super, | |
60 | }; | |
61 | ||
62 | /* | |
63 | * ocxlflash_release_mapping() - release the memory mapping | |
64 | * @ctx: Context whose mapping is to be released. | |
65 | */ | |
66 | static void ocxlflash_release_mapping(struct ocxlflash_context *ctx) | |
67 | { | |
68 | if (ctx->mapping) | |
69 | simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); | |
70 | ctx->mapping = NULL; | |
71 | } | |
72 | ||
73 | /* | |
74 | * ocxlflash_getfile() - allocate pseudo filesystem, inode, and the file | |
75 | * @dev: Generic device of the host. | |
76 | * @name: Name of the pseudo filesystem. | |
77 | * @fops: File operations. | |
78 | * @priv: Private data. | |
79 | * @flags: Flags for the file. | |
80 | * | |
81 | * Return: pointer to the file on success, ERR_PTR on failure | |
82 | */ | |
83 | static struct file *ocxlflash_getfile(struct device *dev, const char *name, | |
84 | const struct file_operations *fops, | |
85 | void *priv, int flags) | |
86 | { | |
87 | struct qstr this; | |
88 | struct path path; | |
89 | struct file *file; | |
90 | struct inode *inode = NULL; | |
91 | int rc; | |
92 | ||
93 | if (fops->owner && !try_module_get(fops->owner)) { | |
94 | dev_err(dev, "%s: Owner does not exist\n", __func__); | |
95 | rc = -ENOENT; | |
96 | goto err1; | |
97 | } | |
98 | ||
99 | rc = simple_pin_fs(&ocxlflash_fs_type, &ocxlflash_vfs_mount, | |
100 | &ocxlflash_fs_cnt); | |
101 | if (unlikely(rc < 0)) { | |
102 | dev_err(dev, "%s: Cannot mount ocxlflash pseudofs rc=%d\n", | |
103 | __func__, rc); | |
104 | goto err2; | |
105 | } | |
106 | ||
107 | inode = alloc_anon_inode(ocxlflash_vfs_mount->mnt_sb); | |
108 | if (IS_ERR(inode)) { | |
109 | rc = PTR_ERR(inode); | |
110 | dev_err(dev, "%s: alloc_anon_inode failed rc=%d\n", | |
111 | __func__, rc); | |
112 | goto err3; | |
113 | } | |
114 | ||
115 | this.name = name; | |
116 | this.len = strlen(name); | |
117 | this.hash = 0; | |
118 | path.dentry = d_alloc_pseudo(ocxlflash_vfs_mount->mnt_sb, &this); | |
119 | if (!path.dentry) { | |
120 | dev_err(dev, "%s: d_alloc_pseudo failed\n", __func__); | |
121 | rc = -ENOMEM; | |
122 | goto err4; | |
123 | } | |
124 | ||
125 | path.mnt = mntget(ocxlflash_vfs_mount); | |
126 | d_instantiate(path.dentry, inode); | |
127 | ||
128 | file = alloc_file(&path, OPEN_FMODE(flags), fops); | |
129 | if (IS_ERR(file)) { | |
130 | rc = PTR_ERR(file); | |
131 | dev_err(dev, "%s: alloc_file failed rc=%d\n", | |
132 | __func__, rc); | |
133 | goto err5; | |
134 | } | |
135 | ||
136 | file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); | |
137 | file->private_data = priv; | |
138 | out: | |
139 | return file; | |
140 | err5: | |
141 | path_put(&path); | |
142 | err4: | |
143 | iput(inode); | |
144 | err3: | |
145 | simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); | |
146 | err2: | |
147 | module_put(fops->owner); | |
148 | err1: | |
149 | file = ERR_PTR(rc); | |
150 | goto out; | |
151 | } | |
152 | ||
012f394c UK |
153 | /** |
154 | * ocxlflash_psa_map() - map the process specific MMIO space | |
155 | * @ctx_cookie: Adapter context for which the mapping needs to be done. | |
156 | * | |
157 | * Return: MMIO pointer of the mapped region | |
158 | */ | |
159 | static void __iomem *ocxlflash_psa_map(void *ctx_cookie) | |
160 | { | |
161 | struct ocxlflash_context *ctx = ctx_cookie; | |
162 | ||
163 | return ioremap(ctx->psn_phys, ctx->psn_size); | |
164 | } | |
165 | ||
166 | /** | |
167 | * ocxlflash_psa_unmap() - unmap the process specific MMIO space | |
168 | * @addr: MMIO pointer to unmap. | |
169 | */ | |
170 | static void ocxlflash_psa_unmap(void __iomem *addr) | |
171 | { | |
172 | iounmap(addr); | |
173 | } | |
174 | ||
b18718c6 UK |
175 | /** |
176 | * ocxlflash_process_element() - get process element of the adapter context | |
177 | * @ctx_cookie: Adapter context associated with the process element. | |
178 | * | |
179 | * Return: process element of the adapter context | |
180 | */ | |
181 | static int ocxlflash_process_element(void *ctx_cookie) | |
182 | { | |
183 | struct ocxlflash_context *ctx = ctx_cookie; | |
184 | ||
185 | return ctx->pe; | |
186 | } | |
187 | ||
6b938ac9 UK |
188 | /** |
189 | * start_context() - local routine to start a context | |
190 | * @ctx: Adapter context to be started. | |
191 | * | |
c207b571 | 192 | * Assign the context specific MMIO space, add and enable the PE. |
6b938ac9 UK |
193 | * |
194 | * Return: 0 on success, -errno on failure | |
195 | */ | |
196 | static int start_context(struct ocxlflash_context *ctx) | |
197 | { | |
198 | struct ocxl_hw_afu *afu = ctx->hw_afu; | |
199 | struct ocxl_afu_config *acfg = &afu->acfg; | |
c207b571 UK |
200 | void *link_token = afu->link_token; |
201 | struct device *dev = afu->dev; | |
6b938ac9 | 202 | bool master = ctx->master; |
c207b571 | 203 | int rc = 0; |
6b938ac9 UK |
204 | |
205 | if (master) { | |
206 | ctx->psn_size = acfg->global_mmio_size; | |
207 | ctx->psn_phys = afu->gmmio_phys; | |
208 | } else { | |
209 | ctx->psn_size = acfg->pp_mmio_stride; | |
210 | ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); | |
211 | } | |
212 | ||
c207b571 UK |
213 | |
214 | /* pid, tid, amr and mm are zeroes/NULL for a kernel context */ | |
215 | rc = ocxl_link_add_pe(link_token, ctx->pe, 0, 0, 0, NULL, NULL, NULL); | |
216 | if (unlikely(rc)) { | |
217 | dev_err(dev, "%s: ocxl_link_add_pe failed rc=%d\n", | |
218 | __func__, rc); | |
219 | goto out; | |
220 | } | |
221 | out: | |
222 | return rc; | |
6b938ac9 UK |
223 | } |
224 | ||
225 | /** | |
226 | * ocxlflash_start_context() - start a kernel context | |
227 | * @ctx_cookie: Adapter context to be started. | |
228 | * | |
229 | * Return: 0 on success, -errno on failure | |
230 | */ | |
231 | static int ocxlflash_start_context(void *ctx_cookie) | |
232 | { | |
233 | struct ocxlflash_context *ctx = ctx_cookie; | |
234 | ||
235 | return start_context(ctx); | |
236 | } | |
237 | ||
c207b571 UK |
238 | /** |
239 | * ocxlflash_stop_context() - stop a context | |
240 | * @ctx_cookie: Adapter context to be stopped. | |
241 | * | |
242 | * Return: 0 on success, -errno on failure | |
243 | */ | |
244 | static int ocxlflash_stop_context(void *ctx_cookie) | |
245 | { | |
246 | struct ocxlflash_context *ctx = ctx_cookie; | |
247 | struct ocxl_hw_afu *afu = ctx->hw_afu; | |
248 | struct ocxl_afu_config *acfg = &afu->acfg; | |
249 | struct pci_dev *pdev = afu->pdev; | |
250 | struct device *dev = afu->dev; | |
251 | int rc; | |
252 | ||
253 | rc = ocxl_config_terminate_pasid(pdev, acfg->dvsec_afu_control_pos, | |
254 | ctx->pe); | |
255 | if (unlikely(rc)) { | |
256 | dev_err(dev, "%s: ocxl_config_terminate_pasid failed rc=%d\n", | |
257 | __func__, rc); | |
258 | /* If EBUSY, PE could be referenced in future by the AFU */ | |
259 | if (rc == -EBUSY) | |
260 | goto out; | |
261 | } | |
262 | ||
263 | rc = ocxl_link_remove_pe(afu->link_token, ctx->pe); | |
264 | if (unlikely(rc)) { | |
265 | dev_err(dev, "%s: ocxl_link_remove_pe failed rc=%d\n", | |
266 | __func__, rc); | |
267 | goto out; | |
268 | } | |
269 | out: | |
270 | return rc; | |
271 | } | |
272 | ||
f6b4557c UK |
273 | /** |
274 | * ocxlflash_set_master() - sets the context as master | |
275 | * @ctx_cookie: Adapter context to set as master. | |
276 | */ | |
277 | static void ocxlflash_set_master(void *ctx_cookie) | |
278 | { | |
279 | struct ocxlflash_context *ctx = ctx_cookie; | |
280 | ||
281 | ctx->master = true; | |
282 | } | |
283 | ||
284 | /** | |
285 | * ocxlflash_get_context() - obtains the context associated with the host | |
286 | * @pdev: PCI device associated with the host. | |
287 | * @afu_cookie: Hardware AFU associated with the host. | |
288 | * | |
289 | * Return: returns the pointer to host adapter context | |
290 | */ | |
291 | static void *ocxlflash_get_context(struct pci_dev *pdev, void *afu_cookie) | |
292 | { | |
293 | struct ocxl_hw_afu *afu = afu_cookie; | |
294 | ||
295 | return afu->ocxl_ctx; | |
296 | } | |
297 | ||
298 | /** | |
299 | * ocxlflash_dev_context_init() - allocate and initialize an adapter context | |
300 | * @pdev: PCI device associated with the host. | |
301 | * @afu_cookie: Hardware AFU associated with the host. | |
302 | * | |
303 | * Return: returns the adapter context on success, ERR_PTR on failure | |
304 | */ | |
305 | static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) | |
306 | { | |
307 | struct ocxl_hw_afu *afu = afu_cookie; | |
308 | struct device *dev = afu->dev; | |
309 | struct ocxlflash_context *ctx; | |
310 | int rc; | |
311 | ||
312 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
313 | if (unlikely(!ctx)) { | |
314 | dev_err(dev, "%s: Context allocation failed\n", __func__); | |
315 | rc = -ENOMEM; | |
429ebfa6 UK |
316 | goto err1; |
317 | } | |
318 | ||
319 | idr_preload(GFP_KERNEL); | |
320 | rc = idr_alloc(&afu->idr, ctx, 0, afu->max_pasid, GFP_NOWAIT); | |
321 | idr_preload_end(); | |
322 | if (unlikely(rc < 0)) { | |
323 | dev_err(dev, "%s: idr_alloc failed rc=%d\n", __func__, rc); | |
324 | goto err2; | |
f6b4557c UK |
325 | } |
326 | ||
429ebfa6 | 327 | ctx->pe = rc; |
f6b4557c | 328 | ctx->master = false; |
926a62f9 | 329 | ctx->mapping = NULL; |
f6b4557c UK |
330 | ctx->hw_afu = afu; |
331 | out: | |
332 | return ctx; | |
429ebfa6 UK |
333 | err2: |
334 | kfree(ctx); | |
335 | err1: | |
f6b4557c UK |
336 | ctx = ERR_PTR(rc); |
337 | goto out; | |
338 | } | |
339 | ||
340 | /** | |
341 | * ocxlflash_release_context() - releases an adapter context | |
342 | * @ctx_cookie: Adapter context to be released. | |
343 | * | |
344 | * Return: 0 on success, -errno on failure | |
345 | */ | |
346 | static int ocxlflash_release_context(void *ctx_cookie) | |
347 | { | |
348 | struct ocxlflash_context *ctx = ctx_cookie; | |
349 | int rc = 0; | |
350 | ||
351 | if (!ctx) | |
352 | goto out; | |
353 | ||
429ebfa6 | 354 | idr_remove(&ctx->hw_afu->idr, ctx->pe); |
926a62f9 | 355 | ocxlflash_release_mapping(ctx); |
f6b4557c UK |
356 | kfree(ctx); |
357 | out: | |
358 | return rc; | |
359 | } | |
360 | ||
8b7a5521 UK |
361 | /** |
362 | * ocxlflash_perst_reloads_same_image() - sets the image reload policy | |
363 | * @afu_cookie: Hardware AFU associated with the host. | |
364 | * @image: Whether to load the same image on PERST. | |
365 | */ | |
366 | static void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) | |
367 | { | |
368 | struct ocxl_hw_afu *afu = afu_cookie; | |
369 | ||
370 | afu->perst_same_image = image; | |
371 | } | |
372 | ||
119c9200 UK |
373 | /** |
374 | * ocxlflash_read_adapter_vpd() - reads the adapter VPD | |
375 | * @pdev: PCI device associated with the host. | |
376 | * @buf: Buffer to get the VPD data. | |
377 | * @count: Size of buffer (maximum bytes that can be read). | |
378 | * | |
379 | * Return: size of VPD on success, -errno on failure | |
380 | */ | |
381 | static ssize_t ocxlflash_read_adapter_vpd(struct pci_dev *pdev, void *buf, | |
382 | size_t count) | |
383 | { | |
384 | return pci_read_vpd(pdev, 0, count, buf); | |
385 | } | |
386 | ||
bc65c1c7 UK |
387 | /** |
388 | * free_afu_irqs() - internal service to free interrupts | |
389 | * @ctx: Adapter context. | |
390 | */ | |
391 | static void free_afu_irqs(struct ocxlflash_context *ctx) | |
392 | { | |
393 | struct ocxl_hw_afu *afu = ctx->hw_afu; | |
394 | struct device *dev = afu->dev; | |
395 | int i; | |
396 | ||
397 | if (!ctx->irqs) { | |
398 | dev_err(dev, "%s: Interrupts not allocated\n", __func__); | |
399 | return; | |
400 | } | |
401 | ||
402 | for (i = ctx->num_irqs; i >= 0; i--) | |
403 | ocxl_link_free_irq(afu->link_token, ctx->irqs[i].hwirq); | |
404 | ||
405 | kfree(ctx->irqs); | |
406 | ctx->irqs = NULL; | |
407 | } | |
408 | ||
409 | /** | |
410 | * alloc_afu_irqs() - internal service to allocate interrupts | |
411 | * @ctx: Context associated with the request. | |
412 | * @num: Number of interrupts requested. | |
413 | * | |
414 | * Return: 0 on success, -errno on failure | |
415 | */ | |
416 | static int alloc_afu_irqs(struct ocxlflash_context *ctx, int num) | |
417 | { | |
418 | struct ocxl_hw_afu *afu = ctx->hw_afu; | |
419 | struct device *dev = afu->dev; | |
420 | struct ocxlflash_irqs *irqs; | |
421 | u64 addr; | |
422 | int rc = 0; | |
423 | int hwirq; | |
424 | int i; | |
425 | ||
426 | if (ctx->irqs) { | |
427 | dev_err(dev, "%s: Interrupts already allocated\n", __func__); | |
428 | rc = -EEXIST; | |
429 | goto out; | |
430 | } | |
431 | ||
432 | if (num > OCXL_MAX_IRQS) { | |
433 | dev_err(dev, "%s: Too many interrupts num=%d\n", __func__, num); | |
434 | rc = -EINVAL; | |
435 | goto out; | |
436 | } | |
437 | ||
438 | irqs = kcalloc(num, sizeof(*irqs), GFP_KERNEL); | |
439 | if (unlikely(!irqs)) { | |
440 | dev_err(dev, "%s: Context irqs allocation failed\n", __func__); | |
441 | rc = -ENOMEM; | |
442 | goto out; | |
443 | } | |
444 | ||
445 | for (i = 0; i < num; i++) { | |
446 | rc = ocxl_link_irq_alloc(afu->link_token, &hwirq, &addr); | |
447 | if (unlikely(rc)) { | |
448 | dev_err(dev, "%s: ocxl_link_irq_alloc failed rc=%d\n", | |
449 | __func__, rc); | |
450 | goto err; | |
451 | } | |
452 | ||
453 | irqs[i].hwirq = hwirq; | |
454 | irqs[i].ptrig = addr; | |
455 | } | |
456 | ||
457 | ctx->irqs = irqs; | |
458 | ctx->num_irqs = num; | |
459 | out: | |
460 | return rc; | |
461 | err: | |
462 | for (i = i-1; i >= 0; i--) | |
463 | ocxl_link_free_irq(afu->link_token, irqs[i].hwirq); | |
464 | kfree(irqs); | |
465 | goto out; | |
466 | } | |
467 | ||
468 | /** | |
469 | * ocxlflash_allocate_afu_irqs() - allocates the requested number of interrupts | |
470 | * @ctx_cookie: Context associated with the request. | |
471 | * @num: Number of interrupts requested. | |
472 | * | |
473 | * Return: 0 on success, -errno on failure | |
474 | */ | |
475 | static int ocxlflash_allocate_afu_irqs(void *ctx_cookie, int num) | |
476 | { | |
477 | return alloc_afu_irqs(ctx_cookie, num); | |
478 | } | |
479 | ||
480 | /** | |
481 | * ocxlflash_free_afu_irqs() - frees the interrupts of an adapter context | |
482 | * @ctx_cookie: Adapter context. | |
483 | */ | |
484 | static void ocxlflash_free_afu_irqs(void *ctx_cookie) | |
485 | { | |
486 | free_afu_irqs(ctx_cookie); | |
487 | } | |
488 | ||
54370503 UK |
489 | /** |
490 | * ocxlflash_unconfig_afu() - unconfigure the AFU | |
491 | * @afu: AFU associated with the host. | |
492 | */ | |
493 | static void ocxlflash_unconfig_afu(struct ocxl_hw_afu *afu) | |
494 | { | |
495 | if (afu->gmmio_virt) { | |
496 | iounmap(afu->gmmio_virt); | |
497 | afu->gmmio_virt = NULL; | |
498 | } | |
499 | } | |
500 | ||
48e077db UK |
501 | /** |
502 | * ocxlflash_destroy_afu() - destroy the AFU structure | |
503 | * @afu_cookie: AFU to be freed. | |
504 | */ | |
505 | static void ocxlflash_destroy_afu(void *afu_cookie) | |
506 | { | |
507 | struct ocxl_hw_afu *afu = afu_cookie; | |
3351e4f0 | 508 | int pos; |
48e077db UK |
509 | |
510 | if (!afu) | |
511 | return; | |
512 | ||
f6b4557c | 513 | ocxlflash_release_context(afu->ocxl_ctx); |
429ebfa6 | 514 | idr_destroy(&afu->idr); |
3351e4f0 UK |
515 | |
516 | /* Disable the AFU */ | |
517 | pos = afu->acfg.dvsec_afu_control_pos; | |
518 | ocxl_config_set_afu_state(afu->pdev, pos, 0); | |
519 | ||
54370503 | 520 | ocxlflash_unconfig_afu(afu); |
48e077db UK |
521 | kfree(afu); |
522 | } | |
523 | ||
e9dfceda UK |
524 | /** |
525 | * ocxlflash_config_fn() - configure the host function | |
526 | * @pdev: PCI device associated with the host. | |
527 | * @afu: AFU associated with the host. | |
528 | * | |
529 | * Return: 0 on success, -errno on failure | |
530 | */ | |
531 | static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) | |
532 | { | |
533 | struct ocxl_fn_config *fcfg = &afu->fcfg; | |
534 | struct device *dev = &pdev->dev; | |
2e222779 | 535 | u16 base, enabled, supported; |
e9dfceda UK |
536 | int rc = 0; |
537 | ||
538 | /* Read DVSEC config of the function */ | |
539 | rc = ocxl_config_read_function(pdev, fcfg); | |
540 | if (unlikely(rc)) { | |
541 | dev_err(dev, "%s: ocxl_config_read_function failed rc=%d\n", | |
542 | __func__, rc); | |
543 | goto out; | |
544 | } | |
545 | ||
546 | /* Check if function has AFUs defined, only 1 per function supported */ | |
547 | if (fcfg->max_afu_index >= 0) { | |
548 | afu->is_present = true; | |
549 | if (fcfg->max_afu_index != 0) | |
550 | dev_warn(dev, "%s: Unexpected AFU index value %d\n", | |
551 | __func__, fcfg->max_afu_index); | |
552 | } | |
2e222779 UK |
553 | |
554 | rc = ocxl_config_get_actag_info(pdev, &base, &enabled, &supported); | |
555 | if (unlikely(rc)) { | |
556 | dev_err(dev, "%s: ocxl_config_get_actag_info failed rc=%d\n", | |
557 | __func__, rc); | |
558 | goto out; | |
559 | } | |
560 | ||
561 | afu->fn_actag_base = base; | |
562 | afu->fn_actag_enabled = enabled; | |
563 | ||
564 | ocxl_config_set_actag(pdev, fcfg->dvsec_function_pos, base, enabled); | |
565 | dev_dbg(dev, "%s: Function acTag range base=%u enabled=%u\n", | |
566 | __func__, base, enabled); | |
73904823 UK |
567 | |
568 | rc = ocxl_link_setup(pdev, 0, &afu->link_token); | |
569 | if (unlikely(rc)) { | |
570 | dev_err(dev, "%s: ocxl_link_setup failed rc=%d\n", | |
571 | __func__, rc); | |
572 | goto out; | |
573 | } | |
c52bf5b3 UK |
574 | |
575 | rc = ocxl_config_set_TL(pdev, fcfg->dvsec_tl_pos); | |
576 | if (unlikely(rc)) { | |
577 | dev_err(dev, "%s: ocxl_config_set_TL failed rc=%d\n", | |
578 | __func__, rc); | |
579 | goto err; | |
580 | } | |
e9dfceda UK |
581 | out: |
582 | return rc; | |
c52bf5b3 UK |
583 | err: |
584 | ocxl_link_release(pdev, afu->link_token); | |
585 | goto out; | |
e9dfceda UK |
586 | } |
587 | ||
73904823 UK |
588 | /** |
589 | * ocxlflash_unconfig_fn() - unconfigure the host function | |
590 | * @pdev: PCI device associated with the host. | |
591 | * @afu: AFU associated with the host. | |
592 | */ | |
593 | static void ocxlflash_unconfig_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) | |
594 | { | |
595 | ocxl_link_release(pdev, afu->link_token); | |
596 | } | |
597 | ||
54370503 UK |
598 | /** |
599 | * ocxlflash_map_mmio() - map the AFU MMIO space | |
600 | * @afu: AFU associated with the host. | |
601 | * | |
602 | * Return: 0 on success, -errno on failure | |
603 | */ | |
604 | static int ocxlflash_map_mmio(struct ocxl_hw_afu *afu) | |
605 | { | |
606 | struct ocxl_afu_config *acfg = &afu->acfg; | |
607 | struct pci_dev *pdev = afu->pdev; | |
608 | struct device *dev = afu->dev; | |
609 | phys_addr_t gmmio, ppmmio; | |
610 | int rc = 0; | |
611 | ||
612 | rc = pci_request_region(pdev, acfg->global_mmio_bar, "ocxlflash"); | |
613 | if (unlikely(rc)) { | |
614 | dev_err(dev, "%s: pci_request_region for global failed rc=%d\n", | |
615 | __func__, rc); | |
616 | goto out; | |
617 | } | |
618 | gmmio = pci_resource_start(pdev, acfg->global_mmio_bar); | |
619 | gmmio += acfg->global_mmio_offset; | |
620 | ||
621 | rc = pci_request_region(pdev, acfg->pp_mmio_bar, "ocxlflash"); | |
622 | if (unlikely(rc)) { | |
623 | dev_err(dev, "%s: pci_request_region for pp bar failed rc=%d\n", | |
624 | __func__, rc); | |
625 | goto err1; | |
626 | } | |
627 | ppmmio = pci_resource_start(pdev, acfg->pp_mmio_bar); | |
628 | ppmmio += acfg->pp_mmio_offset; | |
629 | ||
630 | afu->gmmio_virt = ioremap(gmmio, acfg->global_mmio_size); | |
631 | if (unlikely(!afu->gmmio_virt)) { | |
632 | dev_err(dev, "%s: MMIO mapping failed\n", __func__); | |
633 | rc = -ENOMEM; | |
634 | goto err2; | |
635 | } | |
636 | ||
637 | afu->gmmio_phys = gmmio; | |
638 | afu->ppmmio_phys = ppmmio; | |
639 | out: | |
640 | return rc; | |
641 | err2: | |
642 | pci_release_region(pdev, acfg->pp_mmio_bar); | |
643 | err1: | |
644 | pci_release_region(pdev, acfg->global_mmio_bar); | |
645 | goto out; | |
646 | } | |
647 | ||
9cc84291 UK |
648 | /** |
649 | * ocxlflash_config_afu() - configure the host AFU | |
650 | * @pdev: PCI device associated with the host. | |
651 | * @afu: AFU associated with the host. | |
652 | * | |
653 | * Must be called _after_ host function configuration. | |
654 | * | |
655 | * Return: 0 on success, -errno on failure | |
656 | */ | |
657 | static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) | |
658 | { | |
659 | struct ocxl_afu_config *acfg = &afu->acfg; | |
660 | struct ocxl_fn_config *fcfg = &afu->fcfg; | |
661 | struct device *dev = &pdev->dev; | |
d926519e UK |
662 | int count; |
663 | int base; | |
664 | int pos; | |
9cc84291 UK |
665 | int rc = 0; |
666 | ||
667 | /* This HW AFU function does not have any AFUs defined */ | |
668 | if (!afu->is_present) | |
669 | goto out; | |
670 | ||
671 | /* Read AFU config at index 0 */ | |
672 | rc = ocxl_config_read_afu(pdev, fcfg, acfg, 0); | |
673 | if (unlikely(rc)) { | |
674 | dev_err(dev, "%s: ocxl_config_read_afu failed rc=%d\n", | |
675 | __func__, rc); | |
676 | goto out; | |
677 | } | |
d926519e UK |
678 | |
679 | /* Only one AFU per function is supported, so actag_base is same */ | |
680 | base = afu->fn_actag_base; | |
681 | count = min_t(int, acfg->actag_supported, afu->fn_actag_enabled); | |
682 | pos = acfg->dvsec_afu_control_pos; | |
683 | ||
684 | ocxl_config_set_afu_actag(pdev, pos, base, count); | |
685 | dev_dbg(dev, "%s: acTag base=%d enabled=%d\n", __func__, base, count); | |
686 | afu->afu_actag_base = base; | |
687 | afu->afu_actag_enabled = count; | |
41df40d8 UK |
688 | afu->max_pasid = 1 << acfg->pasid_supported_log; |
689 | ||
690 | ocxl_config_set_afu_pasid(pdev, pos, 0, acfg->pasid_supported_log); | |
54370503 UK |
691 | |
692 | rc = ocxlflash_map_mmio(afu); | |
693 | if (unlikely(rc)) { | |
694 | dev_err(dev, "%s: ocxlflash_map_mmio failed rc=%d\n", | |
695 | __func__, rc); | |
696 | goto out; | |
697 | } | |
3351e4f0 UK |
698 | |
699 | /* Enable the AFU */ | |
700 | ocxl_config_set_afu_state(pdev, acfg->dvsec_afu_control_pos, 1); | |
9cc84291 UK |
701 | out: |
702 | return rc; | |
703 | } | |
704 | ||
48e077db UK |
705 | /** |
706 | * ocxlflash_create_afu() - create the AFU for OCXL | |
707 | * @pdev: PCI device associated with the host. | |
708 | * | |
709 | * Return: AFU on success, NULL on failure | |
710 | */ | |
711 | static void *ocxlflash_create_afu(struct pci_dev *pdev) | |
712 | { | |
713 | struct device *dev = &pdev->dev; | |
f6b4557c | 714 | struct ocxlflash_context *ctx; |
48e077db | 715 | struct ocxl_hw_afu *afu; |
e9dfceda | 716 | int rc; |
48e077db UK |
717 | |
718 | afu = kzalloc(sizeof(*afu), GFP_KERNEL); | |
719 | if (unlikely(!afu)) { | |
720 | dev_err(dev, "%s: HW AFU allocation failed\n", __func__); | |
721 | goto out; | |
722 | } | |
723 | ||
724 | afu->pdev = pdev; | |
725 | afu->dev = dev; | |
429ebfa6 | 726 | idr_init(&afu->idr); |
e9dfceda UK |
727 | |
728 | rc = ocxlflash_config_fn(pdev, afu); | |
729 | if (unlikely(rc)) { | |
730 | dev_err(dev, "%s: Function configuration failed rc=%d\n", | |
731 | __func__, rc); | |
732 | goto err1; | |
733 | } | |
9cc84291 UK |
734 | |
735 | rc = ocxlflash_config_afu(pdev, afu); | |
736 | if (unlikely(rc)) { | |
737 | dev_err(dev, "%s: AFU configuration failed rc=%d\n", | |
738 | __func__, rc); | |
73904823 | 739 | goto err2; |
9cc84291 | 740 | } |
f6b4557c UK |
741 | |
742 | ctx = ocxlflash_dev_context_init(pdev, afu); | |
743 | if (IS_ERR(ctx)) { | |
744 | rc = PTR_ERR(ctx); | |
745 | dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", | |
746 | __func__, rc); | |
73904823 | 747 | goto err3; |
f6b4557c UK |
748 | } |
749 | ||
750 | afu->ocxl_ctx = ctx; | |
48e077db UK |
751 | out: |
752 | return afu; | |
73904823 | 753 | err3: |
54370503 | 754 | ocxlflash_unconfig_afu(afu); |
73904823 UK |
755 | err2: |
756 | ocxlflash_unconfig_fn(pdev, afu); | |
e9dfceda | 757 | err1: |
429ebfa6 | 758 | idr_destroy(&afu->idr); |
e9dfceda UK |
759 | kfree(afu); |
760 | afu = NULL; | |
761 | goto out; | |
48e077db | 762 | } |
76ebe01f | 763 | |
926a62f9 UK |
764 | static const struct file_operations ocxl_afu_fops = { |
765 | .owner = THIS_MODULE, | |
766 | }; | |
767 | ||
768 | /** | |
769 | * ocxlflash_get_fd() - get file descriptor for an adapter context | |
770 | * @ctx_cookie: Adapter context. | |
771 | * @fops: File operations to be associated. | |
772 | * @fd: File descriptor to be returned back. | |
773 | * | |
774 | * Return: pointer to the file on success, ERR_PTR on failure | |
775 | */ | |
776 | static struct file *ocxlflash_get_fd(void *ctx_cookie, | |
777 | struct file_operations *fops, int *fd) | |
778 | { | |
779 | struct ocxlflash_context *ctx = ctx_cookie; | |
780 | struct device *dev = ctx->hw_afu->dev; | |
781 | struct file *file; | |
782 | int flags, fdtmp; | |
783 | int rc = 0; | |
784 | char *name = NULL; | |
785 | ||
786 | /* Only allow one fd per context */ | |
787 | if (ctx->mapping) { | |
788 | dev_err(dev, "%s: Context is already mapped to an fd\n", | |
789 | __func__); | |
790 | rc = -EEXIST; | |
791 | goto err1; | |
792 | } | |
793 | ||
794 | flags = O_RDWR | O_CLOEXEC; | |
795 | ||
796 | /* This code is similar to anon_inode_getfd() */ | |
797 | rc = get_unused_fd_flags(flags); | |
798 | if (unlikely(rc < 0)) { | |
799 | dev_err(dev, "%s: get_unused_fd_flags failed rc=%d\n", | |
800 | __func__, rc); | |
801 | goto err1; | |
802 | } | |
803 | fdtmp = rc; | |
804 | ||
805 | /* Use default ops if there is no fops */ | |
806 | if (!fops) | |
807 | fops = (struct file_operations *)&ocxl_afu_fops; | |
808 | ||
809 | name = kasprintf(GFP_KERNEL, "ocxlflash:%d", ctx->pe); | |
810 | file = ocxlflash_getfile(dev, name, fops, ctx, flags); | |
811 | kfree(name); | |
812 | if (IS_ERR(file)) { | |
813 | rc = PTR_ERR(file); | |
814 | dev_err(dev, "%s: ocxlflash_getfile failed rc=%d\n", | |
815 | __func__, rc); | |
816 | goto err2; | |
817 | } | |
818 | ||
819 | ctx->mapping = file->f_mapping; | |
820 | *fd = fdtmp; | |
821 | out: | |
822 | return file; | |
823 | err2: | |
824 | put_unused_fd(fdtmp); | |
825 | err1: | |
826 | file = ERR_PTR(rc); | |
827 | goto out; | |
828 | } | |
829 | ||
b18718c6 UK |
830 | /** |
831 | * ocxlflash_fops_get_context() - get the context associated with the file | |
832 | * @file: File associated with the adapter context. | |
833 | * | |
834 | * Return: pointer to the context | |
835 | */ | |
836 | static void *ocxlflash_fops_get_context(struct file *file) | |
837 | { | |
838 | return file->private_data; | |
839 | } | |
840 | ||
76ebe01f UK |
841 | /* Backend ops to ocxlflash services */ |
842 | const struct cxlflash_backend_ops cxlflash_ocxl_ops = { | |
843 | .module = THIS_MODULE, | |
012f394c UK |
844 | .psa_map = ocxlflash_psa_map, |
845 | .psa_unmap = ocxlflash_psa_unmap, | |
b18718c6 | 846 | .process_element = ocxlflash_process_element, |
6b938ac9 | 847 | .start_context = ocxlflash_start_context, |
c207b571 | 848 | .stop_context = ocxlflash_stop_context, |
f6b4557c UK |
849 | .set_master = ocxlflash_set_master, |
850 | .get_context = ocxlflash_get_context, | |
851 | .dev_context_init = ocxlflash_dev_context_init, | |
852 | .release_context = ocxlflash_release_context, | |
8b7a5521 | 853 | .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, |
119c9200 | 854 | .read_adapter_vpd = ocxlflash_read_adapter_vpd, |
bc65c1c7 UK |
855 | .allocate_afu_irqs = ocxlflash_allocate_afu_irqs, |
856 | .free_afu_irqs = ocxlflash_free_afu_irqs, | |
48e077db UK |
857 | .create_afu = ocxlflash_create_afu, |
858 | .destroy_afu = ocxlflash_destroy_afu, | |
926a62f9 | 859 | .get_fd = ocxlflash_get_fd, |
b18718c6 | 860 | .fops_get_context = ocxlflash_fops_get_context, |
76ebe01f | 861 | }; |