18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * KFD Interrupts.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * AMD GPUs deliver interrupts by pushing an interrupt description onto the
278c2ecf20Sopenharmony_ci * interrupt ring and then sending an interrupt. KGD receives the interrupt
288c2ecf20Sopenharmony_ci * in ISR and sends us a pointer to each new entry on the interrupt ring.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * We generally can't process interrupt-signaled events from ISR, so we call
318c2ecf20Sopenharmony_ci * out to each interrupt client module (currently only the scheduler) to ask if
328c2ecf20Sopenharmony_ci * each interrupt is interesting. If they return true, then it requires further
338c2ecf20Sopenharmony_ci * processing so we copy it to an internal interrupt ring and call each
348c2ecf20Sopenharmony_ci * interrupt client again from a work-queue.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * There's no acknowledgment for the interrupts we use. The hardware simply
378c2ecf20Sopenharmony_ci * queues a new interrupt each time without waiting.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The fixed-size internal queue means that it's possible for us to lose
408c2ecf20Sopenharmony_ci * interrupts because we have no back-pressure to the hardware.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include <linux/slab.h>
448c2ecf20Sopenharmony_ci#include <linux/device.h>
458c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
468c2ecf20Sopenharmony_ci#include "kfd_priv.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define KFD_IH_NUM_ENTRIES 8192
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void interrupt_wq(struct work_struct *);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciint kfd_interrupt_init(struct kfd_dev *kfd)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int r;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	r = kfifo_alloc(&kfd->ih_fifo,
578c2ecf20Sopenharmony_ci		KFD_IH_NUM_ENTRIES * kfd->device_info->ih_ring_entry_size,
588c2ecf20Sopenharmony_ci		GFP_KERNEL);
598c2ecf20Sopenharmony_ci	if (r) {
608c2ecf20Sopenharmony_ci		dev_err(kfd_chardev(), "Failed to allocate IH fifo\n");
618c2ecf20Sopenharmony_ci		return r;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	kfd->ih_wq = alloc_workqueue("KFD IH", WQ_HIGHPRI, 1);
658c2ecf20Sopenharmony_ci	if (unlikely(!kfd->ih_wq)) {
668c2ecf20Sopenharmony_ci		kfifo_free(&kfd->ih_fifo);
678c2ecf20Sopenharmony_ci		dev_err(kfd_chardev(), "Failed to allocate KFD IH workqueue\n");
688c2ecf20Sopenharmony_ci		return -ENOMEM;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	spin_lock_init(&kfd->interrupt_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	INIT_WORK(&kfd->interrupt_work, interrupt_wq);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	kfd->interrupts_active = true;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * After this function returns, the interrupt will be enabled. This
788c2ecf20Sopenharmony_ci	 * barrier ensures that the interrupt running on a different processor
798c2ecf20Sopenharmony_ci	 * sees all the above writes.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	smp_wmb();
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_civoid kfd_interrupt_exit(struct kfd_dev *kfd)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	/*
898c2ecf20Sopenharmony_ci	 * Stop the interrupt handler from writing to the ring and scheduling
908c2ecf20Sopenharmony_ci	 * workqueue items. The spinlock ensures that any interrupt running
918c2ecf20Sopenharmony_ci	 * after we have unlocked sees interrupts_active = false.
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	unsigned long flags;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&kfd->interrupt_lock, flags);
968c2ecf20Sopenharmony_ci	kfd->interrupts_active = false;
978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&kfd->interrupt_lock, flags);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 * flush_work ensures that there are no outstanding
1018c2ecf20Sopenharmony_ci	 * work-queue items that will access interrupt_ring. New work items
1028c2ecf20Sopenharmony_ci	 * can't be created because we stopped interrupt handling above.
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci	flush_workqueue(kfd->ih_wq);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	kfifo_free(&kfd->ih_fifo);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Assumption: single reader/writer. This function is not re-entrant
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cibool enqueue_ih_ring_entry(struct kfd_dev *kfd,	const void *ih_ring_entry)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	int count;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	count = kfifo_in(&kfd->ih_fifo, ih_ring_entry,
1178c2ecf20Sopenharmony_ci				kfd->device_info->ih_ring_entry_size);
1188c2ecf20Sopenharmony_ci	if (count != kfd->device_info->ih_ring_entry_size) {
1198c2ecf20Sopenharmony_ci		dev_err_ratelimited(kfd_chardev(),
1208c2ecf20Sopenharmony_ci			"Interrupt ring overflow, dropping interrupt %d\n",
1218c2ecf20Sopenharmony_ci			count);
1228c2ecf20Sopenharmony_ci		return false;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return true;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/*
1298c2ecf20Sopenharmony_ci * Assumption: single reader/writer. This function is not re-entrant
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_cistatic bool dequeue_ih_ring_entry(struct kfd_dev *kfd, void *ih_ring_entry)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int count;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	count = kfifo_out(&kfd->ih_fifo, ih_ring_entry,
1368c2ecf20Sopenharmony_ci				kfd->device_info->ih_ring_entry_size);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	WARN_ON(count && count != kfd->device_info->ih_ring_entry_size);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return count == kfd->device_info->ih_ring_entry_size;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void interrupt_wq(struct work_struct *work)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct kfd_dev *dev = container_of(work, struct kfd_dev,
1468c2ecf20Sopenharmony_ci						interrupt_work);
1478c2ecf20Sopenharmony_ci	uint32_t ih_ring_entry[KFD_MAX_RING_ENTRY_SIZE];
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (dev->device_info->ih_ring_entry_size > sizeof(ih_ring_entry)) {
1508c2ecf20Sopenharmony_ci		dev_err_once(kfd_chardev(), "Ring entry too small\n");
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	while (dequeue_ih_ring_entry(dev, ih_ring_entry))
1558c2ecf20Sopenharmony_ci		dev->device_info->event_interrupt_class->interrupt_wq(dev,
1568c2ecf20Sopenharmony_ci								ih_ring_entry);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cibool interrupt_is_wanted(struct kfd_dev *dev,
1608c2ecf20Sopenharmony_ci			const uint32_t *ih_ring_entry,
1618c2ecf20Sopenharmony_ci			uint32_t *patched_ihre, bool *flag)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	/* integer and bitwise OR so there is no boolean short-circuiting */
1648c2ecf20Sopenharmony_ci	unsigned int wanted = 0;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	wanted |= dev->device_info->event_interrupt_class->interrupt_isr(dev,
1678c2ecf20Sopenharmony_ci					 ih_ring_entry, patched_ihre, flag);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return wanted != 0;
1708c2ecf20Sopenharmony_ci}
171