1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2014 IBM Corp. |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/rcupdate.h> |
8 | #include <asm/errno.h> |
9 | #include <misc/cxl-base.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_platform.h> |
12 | #include "cxl.h" |
13 | |
14 | /* protected by rcu */ |
15 | static struct cxl_calls *cxl_calls; |
16 | |
17 | atomic_t cxl_use_count = ATOMIC_INIT(0); |
18 | EXPORT_SYMBOL(cxl_use_count); |
19 | |
20 | #ifdef CONFIG_CXL_MODULE |
21 | |
22 | static inline struct cxl_calls *cxl_calls_get(void) |
23 | { |
24 | struct cxl_calls *calls = NULL; |
25 | |
26 | rcu_read_lock(); |
27 | calls = rcu_dereference(cxl_calls); |
28 | if (calls && !try_module_get(calls->owner)) |
29 | calls = NULL; |
30 | rcu_read_unlock(); |
31 | |
32 | return calls; |
33 | } |
34 | |
35 | static inline void cxl_calls_put(struct cxl_calls *calls) |
36 | { |
37 | BUG_ON(calls != cxl_calls); |
38 | |
39 | /* we don't need to rcu this, as we hold a reference to the module */ |
40 | module_put(cxl_calls->owner); |
41 | } |
42 | |
43 | #else /* !defined CONFIG_CXL_MODULE */ |
44 | |
45 | static inline struct cxl_calls *cxl_calls_get(void) |
46 | { |
47 | return cxl_calls; |
48 | } |
49 | |
50 | static inline void cxl_calls_put(struct cxl_calls *calls) { } |
51 | |
52 | #endif /* CONFIG_CXL_MODULE */ |
53 | |
54 | /* AFU refcount management */ |
55 | struct cxl_afu *cxl_afu_get(struct cxl_afu *afu) |
56 | { |
57 | return (get_device(dev: &afu->dev) == NULL) ? NULL : afu; |
58 | } |
59 | EXPORT_SYMBOL_GPL(cxl_afu_get); |
60 | |
61 | void cxl_afu_put(struct cxl_afu *afu) |
62 | { |
63 | put_device(dev: &afu->dev); |
64 | } |
65 | EXPORT_SYMBOL_GPL(cxl_afu_put); |
66 | |
67 | void cxl_slbia(struct mm_struct *mm) |
68 | { |
69 | struct cxl_calls *calls; |
70 | |
71 | calls = cxl_calls_get(); |
72 | if (!calls) |
73 | return; |
74 | |
75 | if (cxl_ctx_in_use()) |
76 | calls->cxl_slbia(mm); |
77 | |
78 | cxl_calls_put(calls); |
79 | } |
80 | |
81 | int register_cxl_calls(struct cxl_calls *calls) |
82 | { |
83 | if (cxl_calls) |
84 | return -EBUSY; |
85 | |
86 | rcu_assign_pointer(cxl_calls, calls); |
87 | return 0; |
88 | } |
89 | EXPORT_SYMBOL_GPL(register_cxl_calls); |
90 | |
91 | void unregister_cxl_calls(struct cxl_calls *calls) |
92 | { |
93 | BUG_ON(cxl_calls->owner != calls->owner); |
94 | RCU_INIT_POINTER(cxl_calls, NULL); |
95 | synchronize_rcu(); |
96 | } |
97 | EXPORT_SYMBOL_GPL(unregister_cxl_calls); |
98 | |
99 | int cxl_update_properties(struct device_node *dn, |
100 | struct property *new_prop) |
101 | { |
102 | return of_update_property(np: dn, newprop: new_prop); |
103 | } |
104 | EXPORT_SYMBOL_GPL(cxl_update_properties); |
105 | |
106 | static int __init cxl_base_init(void) |
107 | { |
108 | struct device_node *np; |
109 | struct platform_device *dev; |
110 | int count = 0; |
111 | |
112 | /* |
113 | * Scan for compatible devices in guest only |
114 | */ |
115 | if (cpu_has_feature(CPU_FTR_HVMODE)) |
116 | return 0; |
117 | |
118 | for_each_compatible_node(np, NULL, "ibm,coherent-platform-facility" ) { |
119 | dev = of_platform_device_create(np, NULL, NULL); |
120 | if (dev) |
121 | count++; |
122 | } |
123 | pr_devel("Found %d cxl device(s)\n" , count); |
124 | return 0; |
125 | } |
126 | device_initcall(cxl_base_init); |
127 | |