162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Tegra host1x Interrupt Management
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010-2021, NVIDIA Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "dev.h"
1162306a36Sopenharmony_ci#include "fence.h"
1262306a36Sopenharmony_ci#include "intr.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
1562306a36Sopenharmony_ci					  struct host1x_syncpt_fence *fence)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct host1x_syncpt_fence *fence_in_list;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	list_for_each_entry_reverse(fence_in_list, &list->list, list) {
2062306a36Sopenharmony_ci		if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
2162306a36Sopenharmony_ci			/* Fence in list is before us, we can insert here */
2262306a36Sopenharmony_ci			list_add(&fence->list, &fence_in_list->list);
2362306a36Sopenharmony_ci			return;
2462306a36Sopenharmony_ci		}
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* Add as first in list */
2862306a36Sopenharmony_ci	list_add(&fence->list, &list->list);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct host1x_syncpt_fence *fence;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (!list_empty(&sp->fences.list)) {
3662306a36Sopenharmony_ci		fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold);
3962306a36Sopenharmony_ci		host1x_hw_intr_enable_syncpt_intr(host, sp->id);
4062306a36Sopenharmony_ci	} else {
4162306a36Sopenharmony_ci		host1x_hw_intr_disable_syncpt_intr(host, sp->id);
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_civoid host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct host1x_fence_list *fence_list = &fence->sp->fences;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	INIT_LIST_HEAD(&fence->list);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	host1x_intr_add_fence_to_list(fence_list, fence);
5262306a36Sopenharmony_ci	host1x_intr_update_hw_state(host, fence->sp);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cibool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct host1x_fence_list *fence_list = &fence->sp->fences;
5862306a36Sopenharmony_ci	unsigned long irqflags;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	spin_lock_irqsave(&fence_list->lock, irqflags);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (list_empty(&fence->list)) {
6362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fence_list->lock, irqflags);
6462306a36Sopenharmony_ci		return false;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	list_del_init(&fence->list);
6862306a36Sopenharmony_ci	host1x_intr_update_hw_state(host, fence->sp);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spin_unlock_irqrestore(&fence_list->lock, irqflags);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return true;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct host1x_syncpt *sp = &host->syncpt[id];
7862306a36Sopenharmony_ci	struct host1x_syncpt_fence *fence, *tmp;
7962306a36Sopenharmony_ci	unsigned int value;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	value = host1x_syncpt_load(sp);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock(&sp->fences.lock);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
8662306a36Sopenharmony_ci		if (((value - fence->threshold) & 0x80000000U) != 0U) {
8762306a36Sopenharmony_ci			/* Fence is not yet expired, we are done */
8862306a36Sopenharmony_ci			break;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		list_del_init(&fence->list);
9262306a36Sopenharmony_ci		host1x_fence_signal(fence);
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Re-enable interrupt if necessary */
9662306a36Sopenharmony_ci	host1x_intr_update_hw_state(host, sp);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	spin_unlock(&sp->fences.lock);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciint host1x_intr_init(struct host1x *host)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	unsigned int id;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	mutex_init(&host->intr_mutex);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) {
10862306a36Sopenharmony_ci		struct host1x_syncpt *syncpt = &host->syncpt[id];
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		spin_lock_init(&syncpt->fences.lock);
11162306a36Sopenharmony_ci		INIT_LIST_HEAD(&syncpt->fences.list);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid host1x_intr_deinit(struct host1x *host)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid host1x_intr_start(struct host1x *host)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	u32 hz = clk_get_rate(host->clk);
12462306a36Sopenharmony_ci	int err;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	mutex_lock(&host->intr_mutex);
12762306a36Sopenharmony_ci	err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000));
12862306a36Sopenharmony_ci	if (err) {
12962306a36Sopenharmony_ci		mutex_unlock(&host->intr_mutex);
13062306a36Sopenharmony_ci		return;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	mutex_unlock(&host->intr_mutex);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_civoid host1x_intr_stop(struct host1x *host)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	host1x_hw_intr_disable_all_syncpt_intrs(host);
13862306a36Sopenharmony_ci}
139