xref: /OK3568_Linux_fs/kernel/drivers/misc/ocxl/afu_irq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun // Copyright 2017 IBM Corp.
3*4882a593Smuzhiyun #include <linux/interrupt.h>
4*4882a593Smuzhiyun #include <asm/pnv-ocxl.h>
5*4882a593Smuzhiyun #include <asm/xive.h>
6*4882a593Smuzhiyun #include "ocxl_internal.h"
7*4882a593Smuzhiyun #include "trace.h"
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun struct afu_irq {
10*4882a593Smuzhiyun 	int id;
11*4882a593Smuzhiyun 	int hw_irq;
12*4882a593Smuzhiyun 	unsigned int virq;
13*4882a593Smuzhiyun 	char *name;
14*4882a593Smuzhiyun 	irqreturn_t (*handler)(void *private);
15*4882a593Smuzhiyun 	void (*free_private)(void *private);
16*4882a593Smuzhiyun 	void *private;
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun 
ocxl_irq_offset_to_id(struct ocxl_context * ctx,u64 offset)19*4882a593Smuzhiyun int ocxl_irq_offset_to_id(struct ocxl_context *ctx, u64 offset)
20*4882a593Smuzhiyun {
21*4882a593Smuzhiyun 	return (offset - ctx->afu->irq_base_offset) >> PAGE_SHIFT;
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun 
ocxl_irq_id_to_offset(struct ocxl_context * ctx,int irq_id)24*4882a593Smuzhiyun u64 ocxl_irq_id_to_offset(struct ocxl_context *ctx, int irq_id)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	return ctx->afu->irq_base_offset + (irq_id << PAGE_SHIFT);
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun 
ocxl_irq_set_handler(struct ocxl_context * ctx,int irq_id,irqreturn_t (* handler)(void * private),void (* free_private)(void * private),void * private)29*4882a593Smuzhiyun int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
30*4882a593Smuzhiyun 		irqreturn_t (*handler)(void *private),
31*4882a593Smuzhiyun 		void (*free_private)(void *private),
32*4882a593Smuzhiyun 		void *private)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	struct afu_irq *irq;
35*4882a593Smuzhiyun 	int rc;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	mutex_lock(&ctx->irq_lock);
38*4882a593Smuzhiyun 	irq = idr_find(&ctx->irq_idr, irq_id);
39*4882a593Smuzhiyun 	if (!irq) {
40*4882a593Smuzhiyun 		rc = -EINVAL;
41*4882a593Smuzhiyun 		goto unlock;
42*4882a593Smuzhiyun 	}
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	irq->handler = handler;
45*4882a593Smuzhiyun 	irq->private = private;
46*4882a593Smuzhiyun 	irq->free_private = free_private;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	rc = 0;
49*4882a593Smuzhiyun 	// Fall through to unlock
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun unlock:
52*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
53*4882a593Smuzhiyun 	return rc;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ocxl_irq_set_handler);
56*4882a593Smuzhiyun 
afu_irq_handler(int virq,void * data)57*4882a593Smuzhiyun static irqreturn_t afu_irq_handler(int virq, void *data)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	struct afu_irq *irq = (struct afu_irq *) data;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	trace_ocxl_afu_irq_receive(virq);
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	if (irq->handler)
64*4882a593Smuzhiyun 		return irq->handler(irq->private);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	return IRQ_HANDLED; // Just drop it on the ground
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
setup_afu_irq(struct ocxl_context * ctx,struct afu_irq * irq)69*4882a593Smuzhiyun static int setup_afu_irq(struct ocxl_context *ctx, struct afu_irq *irq)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	int rc;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	irq->virq = irq_create_mapping(NULL, irq->hw_irq);
74*4882a593Smuzhiyun 	if (!irq->virq) {
75*4882a593Smuzhiyun 		pr_err("irq_create_mapping failed\n");
76*4882a593Smuzhiyun 		return -ENOMEM;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 	pr_debug("hw_irq %d mapped to virq %u\n", irq->hw_irq, irq->virq);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	irq->name = kasprintf(GFP_KERNEL, "ocxl-afu-%u", irq->virq);
81*4882a593Smuzhiyun 	if (!irq->name) {
82*4882a593Smuzhiyun 		irq_dispose_mapping(irq->virq);
83*4882a593Smuzhiyun 		return -ENOMEM;
84*4882a593Smuzhiyun 	}
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	rc = request_irq(irq->virq, afu_irq_handler, 0, irq->name, irq);
87*4882a593Smuzhiyun 	if (rc) {
88*4882a593Smuzhiyun 		kfree(irq->name);
89*4882a593Smuzhiyun 		irq->name = NULL;
90*4882a593Smuzhiyun 		irq_dispose_mapping(irq->virq);
91*4882a593Smuzhiyun 		pr_err("request_irq failed: %d\n", rc);
92*4882a593Smuzhiyun 		return rc;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 	return 0;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
release_afu_irq(struct afu_irq * irq)97*4882a593Smuzhiyun static void release_afu_irq(struct afu_irq *irq)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	free_irq(irq->virq, irq);
100*4882a593Smuzhiyun 	irq_dispose_mapping(irq->virq);
101*4882a593Smuzhiyun 	kfree(irq->name);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
ocxl_afu_irq_alloc(struct ocxl_context * ctx,int * irq_id)104*4882a593Smuzhiyun int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	struct afu_irq *irq;
107*4882a593Smuzhiyun 	int rc;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	irq = kzalloc(sizeof(struct afu_irq), GFP_KERNEL);
110*4882a593Smuzhiyun 	if (!irq)
111*4882a593Smuzhiyun 		return -ENOMEM;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	/*
114*4882a593Smuzhiyun 	 * We limit the number of afu irqs per context and per link to
115*4882a593Smuzhiyun 	 * avoid a single process or user depleting the pool of IPIs
116*4882a593Smuzhiyun 	 */
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	mutex_lock(&ctx->irq_lock);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	irq->id = idr_alloc(&ctx->irq_idr, irq, 0, MAX_IRQ_PER_CONTEXT,
121*4882a593Smuzhiyun 			GFP_KERNEL);
122*4882a593Smuzhiyun 	if (irq->id < 0) {
123*4882a593Smuzhiyun 		rc = -ENOSPC;
124*4882a593Smuzhiyun 		goto err_unlock;
125*4882a593Smuzhiyun 	}
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	rc = ocxl_link_irq_alloc(ctx->afu->fn->link, &irq->hw_irq);
128*4882a593Smuzhiyun 	if (rc)
129*4882a593Smuzhiyun 		goto err_idr;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	rc = setup_afu_irq(ctx, irq);
132*4882a593Smuzhiyun 	if (rc)
133*4882a593Smuzhiyun 		goto err_alloc;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	trace_ocxl_afu_irq_alloc(ctx->pasid, irq->id, irq->virq, irq->hw_irq);
136*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	*irq_id = irq->id;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	return 0;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun err_alloc:
143*4882a593Smuzhiyun 	ocxl_link_free_irq(ctx->afu->fn->link, irq->hw_irq);
144*4882a593Smuzhiyun err_idr:
145*4882a593Smuzhiyun 	idr_remove(&ctx->irq_idr, irq->id);
146*4882a593Smuzhiyun err_unlock:
147*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
148*4882a593Smuzhiyun 	kfree(irq);
149*4882a593Smuzhiyun 	return rc;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ocxl_afu_irq_alloc);
152*4882a593Smuzhiyun 
afu_irq_free(struct afu_irq * irq,struct ocxl_context * ctx)153*4882a593Smuzhiyun static void afu_irq_free(struct afu_irq *irq, struct ocxl_context *ctx)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	trace_ocxl_afu_irq_free(ctx->pasid, irq->id);
156*4882a593Smuzhiyun 	if (ctx->mapping)
157*4882a593Smuzhiyun 		unmap_mapping_range(ctx->mapping,
158*4882a593Smuzhiyun 				ocxl_irq_id_to_offset(ctx, irq->id),
159*4882a593Smuzhiyun 				1 << PAGE_SHIFT, 1);
160*4882a593Smuzhiyun 	release_afu_irq(irq);
161*4882a593Smuzhiyun 	if (irq->free_private)
162*4882a593Smuzhiyun 		irq->free_private(irq->private);
163*4882a593Smuzhiyun 	ocxl_link_free_irq(ctx->afu->fn->link, irq->hw_irq);
164*4882a593Smuzhiyun 	kfree(irq);
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun 
ocxl_afu_irq_free(struct ocxl_context * ctx,int irq_id)167*4882a593Smuzhiyun int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	struct afu_irq *irq;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	mutex_lock(&ctx->irq_lock);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	irq = idr_find(&ctx->irq_idr, irq_id);
174*4882a593Smuzhiyun 	if (!irq) {
175*4882a593Smuzhiyun 		mutex_unlock(&ctx->irq_lock);
176*4882a593Smuzhiyun 		return -EINVAL;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 	idr_remove(&ctx->irq_idr, irq->id);
179*4882a593Smuzhiyun 	afu_irq_free(irq, ctx);
180*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
181*4882a593Smuzhiyun 	return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ocxl_afu_irq_free);
184*4882a593Smuzhiyun 
ocxl_afu_irq_free_all(struct ocxl_context * ctx)185*4882a593Smuzhiyun void ocxl_afu_irq_free_all(struct ocxl_context *ctx)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct afu_irq *irq;
188*4882a593Smuzhiyun 	int id;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	mutex_lock(&ctx->irq_lock);
191*4882a593Smuzhiyun 	idr_for_each_entry(&ctx->irq_idr, irq, id)
192*4882a593Smuzhiyun 		afu_irq_free(irq, ctx);
193*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
ocxl_afu_irq_get_addr(struct ocxl_context * ctx,int irq_id)196*4882a593Smuzhiyun u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	struct xive_irq_data *xd;
199*4882a593Smuzhiyun 	struct afu_irq *irq;
200*4882a593Smuzhiyun 	u64 addr = 0;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	mutex_lock(&ctx->irq_lock);
203*4882a593Smuzhiyun 	irq = idr_find(&ctx->irq_idr, irq_id);
204*4882a593Smuzhiyun 	if (irq) {
205*4882a593Smuzhiyun 		xd = irq_get_handler_data(irq->virq);
206*4882a593Smuzhiyun 		addr = xd ? xd->trig_page : 0;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 	mutex_unlock(&ctx->irq_lock);
209*4882a593Smuzhiyun 	return addr;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ocxl_afu_irq_get_addr);
212