#include <linux/dma-mapping.h>
#include "amdgpu.h"
#include "amdgpu_ih.h"
int amdgpu_ih_ring_init(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih,
unsigned ring_size, bool use_bus_addr)
{
u32 rb_bufsz;
int r;
rb_bufsz = order_base_2(ring_size / 4);
ring_size = (1 << rb_bufsz) * 4;
ih->ring_size = ring_size;
ih->ptr_mask = ih->ring_size - 1;
ih->rptr = 0;
ih->use_bus_addr = use_bus_addr;
if (use_bus_addr) {
dma_addr_t dma_addr;
if (ih->ring)
return 0;
ih->ring = dma_alloc_coherent(adev->dev, ih->ring_size + 8,
&dma_addr, GFP_KERNEL);
if (ih->ring == NULL)
return -ENOMEM;
ih->gpu_addr = dma_addr;
ih->wptr_addr = dma_addr + ih->ring_size;
ih->wptr_cpu = &ih->ring[ih->ring_size / 4];
ih->rptr_addr = dma_addr + ih->ring_size + 4;
ih->rptr_cpu = &ih->ring[(ih->ring_size / 4) + 1];
} else {
unsigned wptr_offs, rptr_offs;
r = amdgpu_device_wb_get(adev, &wptr_offs);
if (r)
return r;
r = amdgpu_device_wb_get(adev, &rptr_offs);
if (r) {
amdgpu_device_wb_free(adev, wptr_offs);
return r;
}
r = amdgpu_bo_create_kernel(adev, ih->ring_size, PAGE_SIZE,
AMDGPU_GEM_DOMAIN_GTT,
&ih->ring_obj, &ih->gpu_addr,
(void **)&ih->ring);
if (r) {
amdgpu_device_wb_free(adev, rptr_offs);
amdgpu_device_wb_free(adev, wptr_offs);
return r;
}
ih->wptr_addr = adev->wb.gpu_addr + wptr_offs * 4;
ih->wptr_cpu = &adev->wb.wb[wptr_offs];
ih->rptr_addr = adev->wb.gpu_addr + rptr_offs * 4;
ih->rptr_cpu = &adev->wb.wb[rptr_offs];
}
init_waitqueue_head(&ih->wait_process);
return 0;
}
void amdgpu_ih_ring_fini(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{
if (!ih->ring)
return;
if (ih->use_bus_addr) {
dma_free_coherent(adev->dev, ih->ring_size + 8,
(void *)ih->ring, ih->gpu_addr);
ih->ring = NULL;
} else {
amdgpu_bo_free_kernel(&ih->ring_obj, &ih->gpu_addr,
(void **)&ih->ring);
amdgpu_device_wb_free(adev, (ih->wptr_addr - ih->gpu_addr) / 4);
amdgpu_device_wb_free(adev, (ih->rptr_addr - ih->gpu_addr) / 4);
}
}
void amdgpu_ih_ring_write(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih,
const uint32_t *iv, unsigned int num_dw)
{
uint32_t wptr = le32_to_cpu(*ih->wptr_cpu) >> 2;
unsigned int i;
for (i = 0; i < num_dw; ++i)
ih->ring[wptr++] = cpu_to_le32(iv[i]);
wptr <<= 2;
wptr &= ih->ptr_mask;
if (wptr != READ_ONCE(ih->rptr)) {
wmb();
WRITE_ONCE(*ih->wptr_cpu, cpu_to_le32(wptr));
} else if (adev->irq.retry_cam_enabled) {
dev_warn_once(adev->dev, "IH soft ring buffer overflow 0x%X, 0x%X\n",
wptr, ih->rptr);
}
}
int amdgpu_ih_wait_on_checkpoint_process_ts(struct amdgpu_device *adev,
struct amdgpu_ih_ring *ih)
{
uint32_t checkpoint_wptr;
uint64_t checkpoint_ts;
long timeout = HZ;
if (!ih->enabled || adev->shutdown)
return -ENODEV;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih);
rmb();
checkpoint_ts = amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1);
return wait_event_interruptible_timeout(ih->wait_process,
amdgpu_ih_ts_after(checkpoint_ts, ih->processed_timestamp) ||
ih->rptr == amdgpu_ih_get_wptr(adev, ih), timeout);
}
int amdgpu_ih_process(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{
unsigned int count;
u32 wptr;
if (!ih->enabled || adev->shutdown)
return IRQ_NONE;
wptr = amdgpu_ih_get_wptr(adev, ih);
restart_ih:
count = AMDGPU_IH_MAX_NUM_IVS;
DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, ih->rptr, wptr);
rmb();
while (ih->rptr != wptr && --count) {
amdgpu_irq_dispatch(adev, ih);
ih->rptr &= ih->ptr_mask;
}
amdgpu_ih_set_rptr(adev, ih);
wake_up_all(&ih->wait_process);
wptr = amdgpu_ih_get_wptr(adev, ih);
if (wptr != ih->rptr)
goto restart_ih;
return IRQ_HANDLED;
}
void amdgpu_ih_decode_iv_helper(struct amdgpu_device *adev,
struct amdgpu_ih_ring *ih,
struct amdgpu_iv_entry *entry)
{
u32 ring_index = ih->rptr >> 2;
uint32_t dw[8];
dw[0] = le32_to_cpu(ih->ring[ring_index + 0]);
dw[1] = le32_to_cpu(ih->ring[ring_index + 1]);
dw[2] = le32_to_cpu(ih->ring[ring_index + 2]);
dw[3] = le32_to_cpu(ih->ring[ring_index + 3]);
dw[4] = le32_to_cpu(ih->ring[ring_index + 4]);
dw[5] = le32_to_cpu(ih->ring[ring_index + 5]);
dw[6] = le32_to_cpu(ih->ring[ring_index + 6]);
dw[7] = le32_to_cpu(ih->ring[ring_index + 7]);
entry->client_id = dw[0] & 0xff;
entry->src_id = (dw[0] >> 8) & 0xff;
entry->ring_id = (dw[0] >> 16) & 0xff;
entry->vmid = (dw[0] >> 24) & 0xf;
entry->vmid_src = (dw[0] >> 31);
entry->timestamp = dw[1] | ((u64)(dw[2] & 0xffff) << 32);
entry->timestamp_src = dw[2] >> 31;
entry->pasid = dw[3] & 0xffff;
entry->node_id = (dw[3] >> 16) & 0xff;
entry->src_data[0] = dw[4];
entry->src_data[1] = dw[5];
entry->src_data[2] = dw[6];
entry->src_data[3] = dw[7];
ih->rptr += 32;
}
uint64_t amdgpu_ih_decode_iv_ts_helper(struct amdgpu_ih_ring *ih, u32 rptr,
signed int offset)
{
uint32_t iv_size = 32;
uint32_t ring_index;
uint32_t dw1, dw2;
rptr += iv_size * offset;
ring_index = (rptr & ih->ptr_mask) >> 2;
dw1 = le32_to_cpu(ih->ring[ring_index + 1]);
dw2 = le32_to_cpu(ih->ring[ring_index + 2]);
return dw1 | ((u64)(dw2 & 0xffff) << 32);
}