cregit-Linux how code gets into the kernel

Release 4.11 drivers/gpu/host1x/intr.c

/*
 * Tegra host1x Interrupt Management
 *
 * Copyright (c) 2010-2013, NVIDIA Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irq.h>

#include <trace/events/host1x.h>
#include "channel.h"
#include "dev.h"
#include "intr.h"

/* Wait list management */


enum waitlist_state {
	
WLS_PENDING,
	
WLS_REMOVED,
	
WLS_CANCELLED,
	
WLS_HANDLED
};


static void waiter_release(struct kref *kref) { kfree(container_of(kref, struct host1x_waitlist, refcount)); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom24100.00%1100.00%
Total24100.00%1100.00%

/* * add a waiter to a waiter queue, sorted by threshold * returns true if it was added at the head of the queue */
static bool add_waiter_to_queue(struct host1x_waitlist *waiter, struct list_head *queue) { struct host1x_waitlist *pos; u32 thresh = waiter->thresh; list_for_each_entry_reverse(pos, queue, list) if ((s32)(pos->thresh - thresh) <= 0) { list_add(&waiter->list, &pos->list); return false; } list_add(&waiter->list, queue); return true; }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom82100.00%1100.00%
Total82100.00%1100.00%

/* * run through a waiter queue for a single sync point ID * and gather all completed waiters into lists by actions */
static void remove_completed_waiters(struct list_head *head, u32 sync, struct list_head completed[HOST1X_INTR_ACTION_COUNT]) { struct list_head *dest; struct host1x_waitlist *waiter, *next, *prev; list_for_each_entry_safe(waiter, next, head, list) { if ((s32)(waiter->thresh - sync) > 0) break; dest = completed + waiter->action; /* consolidate submit cleanups */ if (waiter->action == HOST1X_INTR_ACTION_SUBMIT_COMPLETE && !list_empty(dest)) { prev = list_entry(dest->prev, struct host1x_waitlist, list); if (prev->data == waiter->data) { prev->count++; dest = NULL; } } /* PENDING->REMOVED or CANCELLED->HANDLED */ if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) { list_del(&waiter->list); kref_put(&waiter->refcount, waiter_release); } else list_move_tail(&waiter->list, dest); } }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom172100.00%2100.00%
Total172100.00%2100.00%


static void reset_threshold_interrupt(struct host1x *host, struct list_head *head, unsigned int id) { u32 thresh = list_first_entry(head, struct host1x_waitlist, list)->thresh; host1x_hw_intr_set_syncpt_threshold(host, id, thresh); host1x_hw_intr_enable_syncpt_intr(host, id); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom51100.00%1100.00%
Total51100.00%1100.00%


static void action_submit_complete(struct host1x_waitlist *waiter) { struct host1x_channel *channel = waiter->data; host1x_cdma_update(&channel->cdma); /* Add nr_completed to trace */ trace_host1x_channel_submit_complete(dev_name(channel->dev), waiter->count, waiter->thresh); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom47100.00%1100.00%
Total47100.00%1100.00%


static void action_wakeup(struct host1x_waitlist *waiter) { wait_queue_head_t *wq = waiter->data; wake_up(wq); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom24100.00%1100.00%
Total24100.00%1100.00%


static void action_wakeup_interruptible(struct host1x_waitlist *waiter) { wait_queue_head_t *wq = waiter->data; wake_up_interruptible(wq); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom24100.00%1100.00%
Total24100.00%1100.00%

typedef void (*action_handler)(struct host1x_waitlist *waiter); static const action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = { action_submit_complete, action_wakeup, action_wakeup_interruptible, };
static void run_handlers(struct list_head completed[HOST1X_INTR_ACTION_COUNT]) { struct list_head *head = completed; int i; for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i, ++head) { action_handler handler = action_handlers[i]; struct host1x_waitlist *waiter, *next; list_for_each_entry_safe(waiter, next, head, list) { list_del(&waiter->list); handler(waiter); WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) != WLS_REMOVED); kref_put(&waiter->refcount, waiter_release); } } }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom107100.00%1100.00%
Total107100.00%1100.00%

/* * Remove & handle all waiters that have completed for the given syncpt */
static int process_wait_list(struct host1x *host, struct host1x_syncpt *syncpt, u32 threshold) { struct list_head completed[HOST1X_INTR_ACTION_COUNT]; unsigned int i; int empty; for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i) INIT_LIST_HEAD(completed + i); spin_lock(&syncpt->intr.lock); remove_completed_waiters(&syncpt->intr.wait_head, threshold, completed); empty = list_empty(&syncpt->intr.wait_head); if (empty) host1x_hw_intr_disable_syncpt_intr(host, syncpt->id); else reset_threshold_interrupt(host, &syncpt->intr.wait_head, syncpt->id); spin_unlock(&syncpt->intr.lock); run_handlers(completed); return empty; }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom137100.00%1100.00%
Total137100.00%1100.00%

/* * Sync point threshold interrupt service thread function * Handles sync point threshold triggers, in thread context */
static void syncpt_thresh_work(struct work_struct *work) { struct host1x_syncpt_intr *syncpt_intr = container_of(work, struct host1x_syncpt_intr, work); struct host1x_syncpt *syncpt = container_of(syncpt_intr, struct host1x_syncpt, intr); unsigned int id = syncpt->id; struct host1x *host = syncpt->host; (void)process_wait_list(host, syncpt, host1x_syncpt_load(host->syncpt + id)); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom77100.00%1100.00%
Total77100.00%1100.00%


int host1x_intr_add_action(struct host1x *host, unsigned int id, u32 thresh, enum host1x_intr_action action, void *data, struct host1x_waitlist *waiter, void **ref) { struct host1x_syncpt *syncpt; int queue_was_empty; if (waiter == NULL) { pr_warn("%s: NULL waiter\n", __func__); return -EINVAL; } /* initialize a new waiter */ INIT_LIST_HEAD(&waiter->list); kref_init(&waiter->refcount); if (ref) kref_get(&waiter->refcount); waiter->thresh = thresh; waiter->action = action; atomic_set(&waiter->state, WLS_PENDING); waiter->data = data; waiter->count = 1; syncpt = host->syncpt + id; spin_lock(&syncpt->intr.lock); queue_was_empty = list_empty(&syncpt->intr.wait_head); if (add_waiter_to_queue(waiter, &syncpt->intr.wait_head)) { /* added at head of list - new threshold value */ host1x_hw_intr_set_syncpt_threshold(host, id, thresh); /* added as first waiter - enable interrupt */ if (queue_was_empty) host1x_hw_intr_enable_syncpt_intr(host, id); } spin_unlock(&syncpt->intr.lock); if (ref) *ref = waiter; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom21399.07%150.00%
Thierry Reding20.93%150.00%
Total215100.00%2100.00%


void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref) { struct host1x_waitlist *waiter = ref; struct host1x_syncpt *syncpt; while (atomic_cmpxchg(&waiter->state, WLS_PENDING, WLS_CANCELLED) == WLS_REMOVED) schedule(); syncpt = host->syncpt + id; (void)process_wait_list(host, syncpt, host1x_syncpt_load(host->syncpt + id)); kref_put(&waiter->refcount, waiter_release); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom8497.67%150.00%
Thierry Reding22.33%150.00%
Total86100.00%2100.00%


int host1x_intr_init(struct host1x *host, unsigned int irq_sync) { unsigned int id; u32 nb_pts = host1x_syncpt_nb_pts(host); mutex_init(&host->intr_mutex); host->intr_syncpt_irq = irq_sync; for (id = 0; id < nb_pts; ++id) { struct host1x_syncpt *syncpt = host->syncpt + id; spin_lock_init(&syncpt->intr.lock); INIT_LIST_HEAD(&syncpt->intr.wait_head); snprintf(syncpt->intr.thresh_irq_name, sizeof(syncpt->intr.thresh_irq_name), "host1x_sp_%02u", id); } host1x_intr_start(host); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom11599.14%150.00%
Thierry Reding10.86%150.00%
Total116100.00%2100.00%


void host1x_intr_deinit(struct host1x *host) { host1x_intr_stop(host); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom15100.00%1100.00%
Total15100.00%1100.00%


void host1x_intr_start(struct host1x *host) { u32 hz = clk_get_rate(host->clk); int err; mutex_lock(&host->intr_mutex); err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000), syncpt_thresh_work); if (err) { mutex_unlock(&host->intr_mutex); return; } mutex_unlock(&host->intr_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom70100.00%1100.00%
Total70100.00%1100.00%


void host1x_intr_stop(struct host1x *host) { unsigned int id; struct host1x_syncpt *syncpt = host->syncpt; u32 nb_pts = host1x_syncpt_nb_pts(host); mutex_lock(&host->intr_mutex); host1x_hw_intr_disable_all_syncpt_intrs(host); for (id = 0; id < nb_pts; ++id) { struct host1x_waitlist *waiter, *next; list_for_each_entry_safe(waiter, next, &syncpt[id].intr.wait_head, list) { if (atomic_cmpxchg(&waiter->state, WLS_CANCELLED, WLS_HANDLED) == WLS_CANCELLED) { list_del(&waiter->list); kref_put(&waiter->refcount, waiter_release); } } if (!list_empty(&syncpt[id].intr.wait_head)) { /* output diagnostics */ mutex_unlock(&host->intr_mutex); pr_warn("%s cannot stop syncpt intr id=%u\n", __func__, id); return; } } host1x_hw_intr_free_syncpt_irq(host); mutex_unlock(&host->intr_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom16499.39%150.00%
Thierry Reding10.61%150.00%
Total165100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Terje Bergstrom147699.53%250.00%
Thierry Reding70.47%250.00%
Total1483100.00%4100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.