linux/drivers/gpu/host1x/hw/intr_hw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Tegra host1x Interrupt Management
   4 *
   5 * Copyright (C) 2010 Google, Inc.
   6 * Copyright (c) 2010-2013, NVIDIA Corporation.
   7 */
   8
   9#include <linux/interrupt.h>
  10#include <linux/irq.h>
  11#include <linux/io.h>
  12
  13#include "../intr.h"
  14#include "../dev.h"
  15
  16/*
  17 * Sync point threshold interrupt service function
  18 * Handles sync point threshold triggers, in interrupt context
  19 */
  20static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt)
  21{
  22        unsigned int id = syncpt->id;
  23        struct host1x *host = syncpt->host;
  24
  25        host1x_sync_writel(host, BIT(id % 32),
  26                HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
  27        host1x_sync_writel(host, BIT(id % 32),
  28                HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
  29
  30        schedule_work(&syncpt->intr.work);
  31}
  32
  33static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
  34{
  35        struct host1x *host = dev_id;
  36        unsigned long reg;
  37        unsigned int i, id;
  38
  39        for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
  40                reg = host1x_sync_readl(host,
  41                        HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
  42                for_each_set_bit(id, &reg, 32) {
  43                        struct host1x_syncpt *syncpt =
  44                                host->syncpt + (i * 32 + id);
  45                        host1x_intr_syncpt_handle(syncpt);
  46                }
  47        }
  48
  49        return IRQ_HANDLED;
  50}
  51
  52static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
  53{
  54        unsigned int i;
  55
  56        for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
  57                host1x_sync_writel(host, 0xffffffffu,
  58                        HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
  59                host1x_sync_writel(host, 0xffffffffu,
  60                        HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
  61        }
  62}
  63
  64static void intr_hw_init(struct host1x *host, u32 cpm)
  65{
  66#if HOST1X_HW < 6
  67        /* disable the ip_busy_timeout. this prevents write drops */
  68        host1x_sync_writel(host, 0, HOST1X_SYNC_IP_BUSY_TIMEOUT);
  69
  70        /*
  71         * increase the auto-ack timout to the maximum value. 2d will hang
  72         * otherwise on Tegra2.
  73         */
  74        host1x_sync_writel(host, 0xff, HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
  75
  76        /* update host clocks per usec */
  77        host1x_sync_writel(host, cpm, HOST1X_SYNC_USEC_CLK);
  78#endif
  79}
  80
  81static int
  82_host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
  83                            void (*syncpt_thresh_work)(struct work_struct *))
  84{
  85        unsigned int i;
  86        int err;
  87
  88        host1x_hw_intr_disable_all_syncpt_intrs(host);
  89
  90        for (i = 0; i < host->info->nb_pts; i++)
  91                INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work);
  92
  93        err = devm_request_irq(host->dev, host->intr_syncpt_irq,
  94                               syncpt_thresh_isr, IRQF_SHARED,
  95                               "host1x_syncpt", host);
  96        if (err < 0) {
  97                WARN_ON(1);
  98                return err;
  99        }
 100
 101        intr_hw_init(host, cpm);
 102
 103        return 0;
 104}
 105
 106static void _host1x_intr_set_syncpt_threshold(struct host1x *host,
 107                                              unsigned int id,
 108                                              u32 thresh)
 109{
 110        host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
 111}
 112
 113static void _host1x_intr_enable_syncpt_intr(struct host1x *host,
 114                                            unsigned int id)
 115{
 116        host1x_sync_writel(host, BIT(id % 32),
 117                HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32));
 118}
 119
 120static void _host1x_intr_disable_syncpt_intr(struct host1x *host,
 121                                             unsigned int id)
 122{
 123        host1x_sync_writel(host, BIT(id % 32),
 124                HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
 125        host1x_sync_writel(host, BIT(id % 32),
 126                HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
 127}
 128
 129static int _host1x_free_syncpt_irq(struct host1x *host)
 130{
 131        unsigned int i;
 132
 133        devm_free_irq(host->dev, host->intr_syncpt_irq, host);
 134
 135        for (i = 0; i < host->info->nb_pts; i++)
 136                cancel_work_sync(&host->syncpt[i].intr.work);
 137
 138        return 0;
 139}
 140
 141static const struct host1x_intr_ops host1x_intr_ops = {
 142        .init_host_sync = _host1x_intr_init_host_sync,
 143        .set_syncpt_threshold = _host1x_intr_set_syncpt_threshold,
 144        .enable_syncpt_intr = _host1x_intr_enable_syncpt_intr,
 145        .disable_syncpt_intr = _host1x_intr_disable_syncpt_intr,
 146        .disable_all_syncpt_intrs = _host1x_intr_disable_all_syncpt_intrs,
 147        .free_syncpt_irq = _host1x_free_syncpt_irq,
 148};
 149