Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Tegra host1x Interrupt Management
  4 *
  5 * Copyright (c) 2010-2021, NVIDIA Corporation.
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/interrupt.h>
 10#include "dev.h"
 11#include "fence.h"
 12#include "intr.h"
 13
 14static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
 15					  struct host1x_syncpt_fence *fence)
 16{
 17	struct host1x_syncpt_fence *fence_in_list;
 18
 19	list_for_each_entry_reverse(fence_in_list, &list->list, list) {
 20		if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
 21			/* Fence in list is before us, we can insert here */
 22			list_add(&fence->list, &fence_in_list->list);
 23			return;
 24		}
 25	}
 26
 27	/* Add as first in list */
 28	list_add(&fence->list, &list->list);
 29}
 30
 31static void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp)
 32{
 33	struct host1x_syncpt_fence *fence;
 34
 35	if (!list_empty(&sp->fences.list)) {
 36		fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list);
 37
 38		host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold);
 39		host1x_hw_intr_enable_syncpt_intr(host, sp->id);
 40	} else {
 41		host1x_hw_intr_disable_syncpt_intr(host, sp->id);
 42	}
 43}
 44
 45void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence)
 46{
 47	struct host1x_fence_list *fence_list = &fence->sp->fences;
 48
 49	INIT_LIST_HEAD(&fence->list);
 50
 51	host1x_intr_add_fence_to_list(fence_list, fence);
 52	host1x_intr_update_hw_state(host, fence->sp);
 53}
 54
 55bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence)
 56{
 57	struct host1x_fence_list *fence_list = &fence->sp->fences;
 58	unsigned long irqflags;
 59
 60	spin_lock_irqsave(&fence_list->lock, irqflags);
 61
 62	if (list_empty(&fence->list)) {
 63		spin_unlock_irqrestore(&fence_list->lock, irqflags);
 64		return false;
 65	}
 66
 67	list_del_init(&fence->list);
 68	host1x_intr_update_hw_state(host, fence->sp);
 69
 70	spin_unlock_irqrestore(&fence_list->lock, irqflags);
 71
 72	return true;
 73}
 74
 75void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
 76{
 77	struct host1x_syncpt *sp = &host->syncpt[id];
 78	struct host1x_syncpt_fence *fence, *tmp;
 79	unsigned int value;
 80
 81	value = host1x_syncpt_load(sp);
 82
 83	spin_lock(&sp->fences.lock);
 84
 85	list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
 86		if (((value - fence->threshold) & 0x80000000U) != 0U) {
 87			/* Fence is not yet expired, we are done */
 88			break;
 89		}
 90
 91		list_del_init(&fence->list);
 92		host1x_fence_signal(fence);
 93	}
 94
 95	/* Re-enable interrupt if necessary */
 96	host1x_intr_update_hw_state(host, sp);
 97
 98	spin_unlock(&sp->fences.lock);
 99}
100
101int host1x_intr_init(struct host1x *host)
102{
103	struct host1x_intr_irq_data *irq_data;
104	unsigned int id;
105	int i, err;
106
107	for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) {
108		struct host1x_syncpt *syncpt = &host->syncpt[id];
109
110		spin_lock_init(&syncpt->fences.lock);
111		INIT_LIST_HEAD(&syncpt->fences.list);
112	}
113
114	irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL);
115	if (!irq_data)
116		return -ENOMEM;
117
118	host1x_hw_intr_disable_all_syncpt_intrs(host);
119
120	for (i = 0; i < host->num_syncpt_irqs; i++) {
121		irq_data[i].host = host;
122		irq_data[i].offset = i;
123
124		err = devm_request_irq(host->dev, host->syncpt_irqs[i],
125				       host->intr_op->isr, IRQF_SHARED,
126				       "host1x_syncpt", &irq_data[i]);
127		if (err < 0)
128			return err;
129	}
130
131	return 0;
132}
133
134void host1x_intr_deinit(struct host1x *host)
135{
136}
137
138void host1x_intr_start(struct host1x *host)
139{
140	u32 hz = clk_get_rate(host->clk);
141	int err;
142
143	mutex_lock(&host->intr_mutex);
144	err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000));
145	if (err) {
146		mutex_unlock(&host->intr_mutex);
147		return;
148	}
149	mutex_unlock(&host->intr_mutex);
150}
151
152void host1x_intr_stop(struct host1x *host)
153{
154	host1x_hw_intr_disable_all_syncpt_intrs(host);
155}