xref: /OK3568_Linux_fs/kernel/drivers/dma/idxd/irq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright(c) 2019 Intel Corporation. All rights rsvd. */
3*4882a593Smuzhiyun #include <linux/init.h>
4*4882a593Smuzhiyun #include <linux/kernel.h>
5*4882a593Smuzhiyun #include <linux/module.h>
6*4882a593Smuzhiyun #include <linux/pci.h>
7*4882a593Smuzhiyun #include <linux/io-64-nonatomic-lo-hi.h>
8*4882a593Smuzhiyun #include <linux/dmaengine.h>
9*4882a593Smuzhiyun #include <uapi/linux/idxd.h>
10*4882a593Smuzhiyun #include "../dmaengine.h"
11*4882a593Smuzhiyun #include "idxd.h"
12*4882a593Smuzhiyun #include "registers.h"
13*4882a593Smuzhiyun 
idxd_device_reinit(struct work_struct * work)14*4882a593Smuzhiyun static void idxd_device_reinit(struct work_struct *work)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun 	struct idxd_device *idxd = container_of(work, struct idxd_device, work);
17*4882a593Smuzhiyun 	struct device *dev = &idxd->pdev->dev;
18*4882a593Smuzhiyun 	int rc, i;
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun 	idxd_device_reset(idxd);
21*4882a593Smuzhiyun 	rc = idxd_device_config(idxd);
22*4882a593Smuzhiyun 	if (rc < 0)
23*4882a593Smuzhiyun 		goto out;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 	rc = idxd_device_enable(idxd);
26*4882a593Smuzhiyun 	if (rc < 0)
27*4882a593Smuzhiyun 		goto out;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	for (i = 0; i < idxd->max_wqs; i++) {
30*4882a593Smuzhiyun 		struct idxd_wq *wq = &idxd->wqs[i];
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 		if (wq->state == IDXD_WQ_ENABLED) {
33*4882a593Smuzhiyun 			rc = idxd_wq_enable(wq);
34*4882a593Smuzhiyun 			if (rc < 0) {
35*4882a593Smuzhiyun 				dev_warn(dev, "Unable to re-enable wq %s\n",
36*4882a593Smuzhiyun 					 dev_name(&wq->conf_dev));
37*4882a593Smuzhiyun 			}
38*4882a593Smuzhiyun 		}
39*4882a593Smuzhiyun 	}
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	return;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun  out:
44*4882a593Smuzhiyun 	idxd_device_wqs_clear_state(idxd);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun 
idxd_irq_handler(int vec,void * data)47*4882a593Smuzhiyun irqreturn_t idxd_irq_handler(int vec, void *data)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	struct idxd_irq_entry *irq_entry = data;
50*4882a593Smuzhiyun 	struct idxd_device *idxd = irq_entry->idxd;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	idxd_mask_msix_vector(idxd, irq_entry->id);
53*4882a593Smuzhiyun 	return IRQ_WAKE_THREAD;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun 
process_misc_interrupts(struct idxd_device * idxd,u32 cause)56*4882a593Smuzhiyun static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun 	struct device *dev = &idxd->pdev->dev;
59*4882a593Smuzhiyun 	union gensts_reg gensts;
60*4882a593Smuzhiyun 	u32 val = 0;
61*4882a593Smuzhiyun 	int i;
62*4882a593Smuzhiyun 	bool err = false;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	if (cause & IDXD_INTC_ERR) {
65*4882a593Smuzhiyun 		spin_lock_bh(&idxd->dev_lock);
66*4882a593Smuzhiyun 		for (i = 0; i < 4; i++)
67*4882a593Smuzhiyun 			idxd->sw_err.bits[i] = ioread64(idxd->reg_base +
68*4882a593Smuzhiyun 					IDXD_SWERR_OFFSET + i * sizeof(u64));
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 		iowrite64(idxd->sw_err.bits[0] & IDXD_SWERR_ACK,
71*4882a593Smuzhiyun 			  idxd->reg_base + IDXD_SWERR_OFFSET);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 		if (idxd->sw_err.valid && idxd->sw_err.wq_idx_valid) {
74*4882a593Smuzhiyun 			int id = idxd->sw_err.wq_idx;
75*4882a593Smuzhiyun 			struct idxd_wq *wq = &idxd->wqs[id];
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 			if (wq->type == IDXD_WQT_USER)
78*4882a593Smuzhiyun 				wake_up_interruptible(&wq->err_queue);
79*4882a593Smuzhiyun 		} else {
80*4882a593Smuzhiyun 			int i;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 			for (i = 0; i < idxd->max_wqs; i++) {
83*4882a593Smuzhiyun 				struct idxd_wq *wq = &idxd->wqs[i];
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 				if (wq->type == IDXD_WQT_USER)
86*4882a593Smuzhiyun 					wake_up_interruptible(&wq->err_queue);
87*4882a593Smuzhiyun 			}
88*4882a593Smuzhiyun 		}
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 		spin_unlock_bh(&idxd->dev_lock);
91*4882a593Smuzhiyun 		val |= IDXD_INTC_ERR;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 		for (i = 0; i < 4; i++)
94*4882a593Smuzhiyun 			dev_warn(dev, "err[%d]: %#16.16llx\n",
95*4882a593Smuzhiyun 				 i, idxd->sw_err.bits[i]);
96*4882a593Smuzhiyun 		err = true;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (cause & IDXD_INTC_CMD) {
100*4882a593Smuzhiyun 		val |= IDXD_INTC_CMD;
101*4882a593Smuzhiyun 		complete(idxd->cmd_done);
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (cause & IDXD_INTC_OCCUPY) {
105*4882a593Smuzhiyun 		/* Driver does not utilize occupancy interrupt */
106*4882a593Smuzhiyun 		val |= IDXD_INTC_OCCUPY;
107*4882a593Smuzhiyun 	}
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	if (cause & IDXD_INTC_PERFMON_OVFL) {
110*4882a593Smuzhiyun 		/*
111*4882a593Smuzhiyun 		 * Driver does not utilize perfmon counter overflow interrupt
112*4882a593Smuzhiyun 		 * yet.
113*4882a593Smuzhiyun 		 */
114*4882a593Smuzhiyun 		val |= IDXD_INTC_PERFMON_OVFL;
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	val ^= cause;
118*4882a593Smuzhiyun 	if (val)
119*4882a593Smuzhiyun 		dev_warn_once(dev, "Unexpected interrupt cause bits set: %#x\n",
120*4882a593Smuzhiyun 			      val);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	if (!err)
123*4882a593Smuzhiyun 		return 0;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
126*4882a593Smuzhiyun 	if (gensts.state == IDXD_DEVICE_STATE_HALT) {
127*4882a593Smuzhiyun 		idxd->state = IDXD_DEV_HALTED;
128*4882a593Smuzhiyun 		if (gensts.reset_type == IDXD_DEVICE_RESET_SOFTWARE) {
129*4882a593Smuzhiyun 			/*
130*4882a593Smuzhiyun 			 * If we need a software reset, we will throw the work
131*4882a593Smuzhiyun 			 * on a system workqueue in order to allow interrupts
132*4882a593Smuzhiyun 			 * for the device command completions.
133*4882a593Smuzhiyun 			 */
134*4882a593Smuzhiyun 			INIT_WORK(&idxd->work, idxd_device_reinit);
135*4882a593Smuzhiyun 			queue_work(idxd->wq, &idxd->work);
136*4882a593Smuzhiyun 		} else {
137*4882a593Smuzhiyun 			spin_lock_bh(&idxd->dev_lock);
138*4882a593Smuzhiyun 			idxd_device_wqs_clear_state(idxd);
139*4882a593Smuzhiyun 			dev_err(&idxd->pdev->dev,
140*4882a593Smuzhiyun 				"idxd halted, need %s.\n",
141*4882a593Smuzhiyun 				gensts.reset_type == IDXD_DEVICE_RESET_FLR ?
142*4882a593Smuzhiyun 				"FLR" : "system reset");
143*4882a593Smuzhiyun 			spin_unlock_bh(&idxd->dev_lock);
144*4882a593Smuzhiyun 			return -ENXIO;
145*4882a593Smuzhiyun 		}
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
idxd_misc_thread(int vec,void * data)151*4882a593Smuzhiyun irqreturn_t idxd_misc_thread(int vec, void *data)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct idxd_irq_entry *irq_entry = data;
154*4882a593Smuzhiyun 	struct idxd_device *idxd = irq_entry->idxd;
155*4882a593Smuzhiyun 	int rc;
156*4882a593Smuzhiyun 	u32 cause;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
159*4882a593Smuzhiyun 	if (cause)
160*4882a593Smuzhiyun 		iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	while (cause) {
163*4882a593Smuzhiyun 		rc = process_misc_interrupts(idxd, cause);
164*4882a593Smuzhiyun 		if (rc < 0)
165*4882a593Smuzhiyun 			break;
166*4882a593Smuzhiyun 		cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
167*4882a593Smuzhiyun 		if (cause)
168*4882a593Smuzhiyun 			iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	idxd_unmask_msix_vector(idxd, irq_entry->id);
172*4882a593Smuzhiyun 	return IRQ_HANDLED;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
irq_process_pending_llist(struct idxd_irq_entry * irq_entry,int * processed)175*4882a593Smuzhiyun static int irq_process_pending_llist(struct idxd_irq_entry *irq_entry,
176*4882a593Smuzhiyun 				     int *processed)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	struct idxd_desc *desc, *t;
179*4882a593Smuzhiyun 	struct llist_node *head;
180*4882a593Smuzhiyun 	int queued = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	*processed = 0;
183*4882a593Smuzhiyun 	head = llist_del_all(&irq_entry->pending_llist);
184*4882a593Smuzhiyun 	if (!head)
185*4882a593Smuzhiyun 		return 0;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	llist_for_each_entry_safe(desc, t, head, llnode) {
188*4882a593Smuzhiyun 		if (desc->completion->status) {
189*4882a593Smuzhiyun 			idxd_dma_complete_txd(desc, IDXD_COMPLETE_NORMAL);
190*4882a593Smuzhiyun 			idxd_free_desc(desc->wq, desc);
191*4882a593Smuzhiyun 			(*processed)++;
192*4882a593Smuzhiyun 		} else {
193*4882a593Smuzhiyun 			list_add_tail(&desc->list, &irq_entry->work_list);
194*4882a593Smuzhiyun 			queued++;
195*4882a593Smuzhiyun 		}
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	return queued;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
irq_process_work_list(struct idxd_irq_entry * irq_entry,int * processed)201*4882a593Smuzhiyun static int irq_process_work_list(struct idxd_irq_entry *irq_entry,
202*4882a593Smuzhiyun 				 int *processed)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	struct list_head *node, *next;
205*4882a593Smuzhiyun 	int queued = 0;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	*processed = 0;
208*4882a593Smuzhiyun 	if (list_empty(&irq_entry->work_list))
209*4882a593Smuzhiyun 		return 0;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	list_for_each_safe(node, next, &irq_entry->work_list) {
212*4882a593Smuzhiyun 		struct idxd_desc *desc =
213*4882a593Smuzhiyun 			container_of(node, struct idxd_desc, list);
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 		if (desc->completion->status) {
216*4882a593Smuzhiyun 			list_del(&desc->list);
217*4882a593Smuzhiyun 			/* process and callback */
218*4882a593Smuzhiyun 			idxd_dma_complete_txd(desc, IDXD_COMPLETE_NORMAL);
219*4882a593Smuzhiyun 			idxd_free_desc(desc->wq, desc);
220*4882a593Smuzhiyun 			(*processed)++;
221*4882a593Smuzhiyun 		} else {
222*4882a593Smuzhiyun 			queued++;
223*4882a593Smuzhiyun 		}
224*4882a593Smuzhiyun 	}
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	return queued;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
idxd_desc_process(struct idxd_irq_entry * irq_entry)229*4882a593Smuzhiyun static int idxd_desc_process(struct idxd_irq_entry *irq_entry)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	int rc, processed, total = 0;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	/*
234*4882a593Smuzhiyun 	 * There are two lists we are processing. The pending_llist is where
235*4882a593Smuzhiyun 	 * submmiter adds all the submitted descriptor after sending it to
236*4882a593Smuzhiyun 	 * the workqueue. It's a lockless singly linked list. The work_list
237*4882a593Smuzhiyun 	 * is the common linux double linked list. We are in a scenario of
238*4882a593Smuzhiyun 	 * multiple producers and a single consumer. The producers are all
239*4882a593Smuzhiyun 	 * the kernel submitters of descriptors, and the consumer is the
240*4882a593Smuzhiyun 	 * kernel irq handler thread for the msix vector when using threaded
241*4882a593Smuzhiyun 	 * irq. To work with the restrictions of llist to remain lockless,
242*4882a593Smuzhiyun 	 * we are doing the following steps:
243*4882a593Smuzhiyun 	 * 1. Iterate through the work_list and process any completed
244*4882a593Smuzhiyun 	 *    descriptor. Delete the completed entries during iteration.
245*4882a593Smuzhiyun 	 * 2. llist_del_all() from the pending list.
246*4882a593Smuzhiyun 	 * 3. Iterate through the llist that was deleted from the pending list
247*4882a593Smuzhiyun 	 *    and process the completed entries.
248*4882a593Smuzhiyun 	 * 4. If the entry is still waiting on hardware, list_add_tail() to
249*4882a593Smuzhiyun 	 *    the work_list.
250*4882a593Smuzhiyun 	 * 5. Repeat until no more descriptors.
251*4882a593Smuzhiyun 	 */
252*4882a593Smuzhiyun 	do {
253*4882a593Smuzhiyun 		rc = irq_process_work_list(irq_entry, &processed);
254*4882a593Smuzhiyun 		total += processed;
255*4882a593Smuzhiyun 		if (rc != 0)
256*4882a593Smuzhiyun 			continue;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 		rc = irq_process_pending_llist(irq_entry, &processed);
259*4882a593Smuzhiyun 		total += processed;
260*4882a593Smuzhiyun 	} while (rc != 0);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	return total;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
idxd_wq_thread(int irq,void * data)265*4882a593Smuzhiyun irqreturn_t idxd_wq_thread(int irq, void *data)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	struct idxd_irq_entry *irq_entry = data;
268*4882a593Smuzhiyun 	int processed;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	processed = idxd_desc_process(irq_entry);
271*4882a593Smuzhiyun 	idxd_unmask_msix_vector(irq_entry->idxd, irq_entry->id);
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	if (processed == 0)
274*4882a593Smuzhiyun 		return IRQ_NONE;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	return IRQ_HANDLED;
277*4882a593Smuzhiyun }
278