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