1*c460697dSYicong Yang // SPDX-License-Identifier: GPL-2.0
2*c460697dSYicong Yang /*
3*c460697dSYicong Yang * Generic support for Memory System Cache Maintenance operations.
4*c460697dSYicong Yang *
5*c460697dSYicong Yang * Coherency maintenance drivers register with this simple framework that will
6*c460697dSYicong Yang * iterate over each registered instance to first kick off invalidation and
7*c460697dSYicong Yang * then to wait until it is complete.
8*c460697dSYicong Yang *
9*c460697dSYicong Yang * If no implementations are registered yet cpu_cache_has_invalidate_memregion()
10*c460697dSYicong Yang * will return false. If this runs concurrently with unregistration then a
11*c460697dSYicong Yang * race exists but this is no worse than the case where the operations instance
12*c460697dSYicong Yang * responsible for a given memory region has not yet registered.
13*c460697dSYicong Yang */
14*c460697dSYicong Yang #include <linux/cache_coherency.h>
15*c460697dSYicong Yang #include <linux/cleanup.h>
16*c460697dSYicong Yang #include <linux/container_of.h>
17*c460697dSYicong Yang #include <linux/export.h>
18*c460697dSYicong Yang #include <linux/kref.h>
19*c460697dSYicong Yang #include <linux/list.h>
20*c460697dSYicong Yang #include <linux/memregion.h>
21*c460697dSYicong Yang #include <linux/module.h>
22*c460697dSYicong Yang #include <linux/rwsem.h>
23*c460697dSYicong Yang #include <linux/slab.h>
24*c460697dSYicong Yang
25*c460697dSYicong Yang static LIST_HEAD(cache_ops_instance_list);
26*c460697dSYicong Yang static DECLARE_RWSEM(cache_ops_instance_list_lock);
27*c460697dSYicong Yang
__cache_coherency_ops_instance_free(struct kref * kref)28*c460697dSYicong Yang static void __cache_coherency_ops_instance_free(struct kref *kref)
29*c460697dSYicong Yang {
30*c460697dSYicong Yang struct cache_coherency_ops_inst *cci =
31*c460697dSYicong Yang container_of(kref, struct cache_coherency_ops_inst, kref);
32*c460697dSYicong Yang kfree(cci);
33*c460697dSYicong Yang }
34*c460697dSYicong Yang
cache_coherency_ops_instance_put(struct cache_coherency_ops_inst * cci)35*c460697dSYicong Yang void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci)
36*c460697dSYicong Yang {
37*c460697dSYicong Yang kref_put(&cci->kref, __cache_coherency_ops_instance_free);
38*c460697dSYicong Yang }
39*c460697dSYicong Yang EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put);
40*c460697dSYicong Yang
cache_inval_one(struct cache_coherency_ops_inst * cci,void * data)41*c460697dSYicong Yang static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data)
42*c460697dSYicong Yang {
43*c460697dSYicong Yang if (!cci->ops)
44*c460697dSYicong Yang return -EINVAL;
45*c460697dSYicong Yang
46*c460697dSYicong Yang return cci->ops->wbinv(cci, data);
47*c460697dSYicong Yang }
48*c460697dSYicong Yang
cache_inval_done_one(struct cache_coherency_ops_inst * cci)49*c460697dSYicong Yang static int cache_inval_done_one(struct cache_coherency_ops_inst *cci)
50*c460697dSYicong Yang {
51*c460697dSYicong Yang if (!cci->ops)
52*c460697dSYicong Yang return -EINVAL;
53*c460697dSYicong Yang
54*c460697dSYicong Yang if (!cci->ops->done)
55*c460697dSYicong Yang return 0;
56*c460697dSYicong Yang
57*c460697dSYicong Yang return cci->ops->done(cci);
58*c460697dSYicong Yang }
59*c460697dSYicong Yang
cache_invalidate_memregion(phys_addr_t addr,size_t size)60*c460697dSYicong Yang static int cache_invalidate_memregion(phys_addr_t addr, size_t size)
61*c460697dSYicong Yang {
62*c460697dSYicong Yang int ret;
63*c460697dSYicong Yang struct cache_coherency_ops_inst *cci;
64*c460697dSYicong Yang struct cc_inval_params params = {
65*c460697dSYicong Yang .addr = addr,
66*c460697dSYicong Yang .size = size,
67*c460697dSYicong Yang };
68*c460697dSYicong Yang
69*c460697dSYicong Yang guard(rwsem_read)(&cache_ops_instance_list_lock);
70*c460697dSYicong Yang list_for_each_entry(cci, &cache_ops_instance_list, node) {
71*c460697dSYicong Yang ret = cache_inval_one(cci, ¶ms);
72*c460697dSYicong Yang if (ret)
73*c460697dSYicong Yang return ret;
74*c460697dSYicong Yang }
75*c460697dSYicong Yang list_for_each_entry(cci, &cache_ops_instance_list, node) {
76*c460697dSYicong Yang ret = cache_inval_done_one(cci);
77*c460697dSYicong Yang if (ret)
78*c460697dSYicong Yang return ret;
79*c460697dSYicong Yang }
80*c460697dSYicong Yang
81*c460697dSYicong Yang return 0;
82*c460697dSYicong Yang }
83*c460697dSYicong Yang
84*c460697dSYicong Yang struct cache_coherency_ops_inst *
_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops * ops,size_t size)85*c460697dSYicong Yang _cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops,
86*c460697dSYicong Yang size_t size)
87*c460697dSYicong Yang {
88*c460697dSYicong Yang struct cache_coherency_ops_inst *cci;
89*c460697dSYicong Yang
90*c460697dSYicong Yang if (!ops || !ops->wbinv)
91*c460697dSYicong Yang return NULL;
92*c460697dSYicong Yang
93*c460697dSYicong Yang cci = kzalloc(size, GFP_KERNEL);
94*c460697dSYicong Yang if (!cci)
95*c460697dSYicong Yang return NULL;
96*c460697dSYicong Yang
97*c460697dSYicong Yang cci->ops = ops;
98*c460697dSYicong Yang INIT_LIST_HEAD(&cci->node);
99*c460697dSYicong Yang kref_init(&cci->kref);
100*c460697dSYicong Yang
101*c460697dSYicong Yang return cci;
102*c460697dSYicong Yang }
103*c460697dSYicong Yang EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, "CACHE_COHERENCY");
104*c460697dSYicong Yang
cache_coherency_ops_instance_register(struct cache_coherency_ops_inst * cci)105*c460697dSYicong Yang int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci)
106*c460697dSYicong Yang {
107*c460697dSYicong Yang guard(rwsem_write)(&cache_ops_instance_list_lock);
108*c460697dSYicong Yang list_add(&cci->node, &cache_ops_instance_list);
109*c460697dSYicong Yang
110*c460697dSYicong Yang return 0;
111*c460697dSYicong Yang }
112*c460697dSYicong Yang EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, "CACHE_COHERENCY");
113*c460697dSYicong Yang
cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst * cci)114*c460697dSYicong Yang void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci)
115*c460697dSYicong Yang {
116*c460697dSYicong Yang guard(rwsem_write)(&cache_ops_instance_list_lock);
117*c460697dSYicong Yang list_del(&cci->node);
118*c460697dSYicong Yang }
119*c460697dSYicong Yang EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, "CACHE_COHERENCY");
120*c460697dSYicong Yang
cpu_cache_invalidate_memregion(phys_addr_t start,size_t len)121*c460697dSYicong Yang int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len)
122*c460697dSYicong Yang {
123*c460697dSYicong Yang return cache_invalidate_memregion(start, len);
124*c460697dSYicong Yang }
125*c460697dSYicong Yang EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, "DEVMEM");
126*c460697dSYicong Yang
127*c460697dSYicong Yang /*
128*c460697dSYicong Yang * Used for optimization / debug purposes only as removal can race
129*c460697dSYicong Yang *
130*c460697dSYicong Yang * Machines that do not support invalidation, e.g. VMs, will not have any
131*c460697dSYicong Yang * operations instance to register and so this will always return false.
132*c460697dSYicong Yang */
cpu_cache_has_invalidate_memregion(void)133*c460697dSYicong Yang bool cpu_cache_has_invalidate_memregion(void)
134*c460697dSYicong Yang {
135*c460697dSYicong Yang guard(rwsem_read)(&cache_ops_instance_list_lock);
136*c460697dSYicong Yang return !list_empty(&cache_ops_instance_list);
137*c460697dSYicong Yang }
138*c460697dSYicong Yang EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, "DEVMEM");
139